Win a copy of Secure Financial Transactions with Ansible, Terraform, and OpenSCAP this week in the Cloud/Virtualization forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Ron McLeod
  • Paul Clapham
  • Jeanne Boyarsky
  • Bear Bibeault
Sheriffs:
  • Rob Spoor
  • Henry Wong
  • Liutauras Vilda
Saloon Keepers:
  • Tim Moores
  • Carey Brown
  • Stephan van Hulst
  • Tim Holloway
  • Piet Souris
Bartenders:
  • Frits Walraven
  • Himai Minh
  • Jj Roberts

Is Data-Oriented programming applicable in Java?

 
Author
Posts: 20
5
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I am the author of a book for Manning called Data-Oriented programming (DOP).
The book has been released for early access in February 2021 and is available online.

Data-Oriented programming is a paradigm evangelised by Clojure (a language that runs on the JVM) that can be applied both to OOP and FP languages.

For statically-typed languages like Java, it is quite challenging to apply DOP. (Streams definitely help!).

I'd like to discuss with the Java community the applicability of Data-Oriented programming in Java.

Here is an article that I wrote in order to imagine how DOP could look like in Java. It's just the beginning.

I am not a Java expert and I'd be very interested to discuss how we could embrace Data-Oriented programming with Java experts.
 
Bartender
Posts: 1304
39
IBM DB2 Netbeans IDE Spring Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks for introducing this topic. I've just read your article,  really interesting. Anyway, and of course only as my personal point of view, there are a couple of concept I'm not convinced about.
The first one is the idea of using hash maps to represent data. It's quite a flexible approach, but at the very end you will need to convert such maps into objects, and vice versa, at least if you want to use OOP. An sql cursor can be naturally seen as a list of maps (where the map keys are the column names and the value the attributes of each intersection row/column),  and may be a convenient way to represent data fetched from a dbms, as well as a way to represent data before they are getting written /serialized; despite this, the idea to have an entity (as  ipa does, or jooq does as well) which attributes are directly persisted on a database when a transaction commits,  is more natural in Java. The second point I disagree is about Json representation of objects.Yes, under the hood there's a lot of reflection, but it's not that inefficient thing it used to be - converting from and to Json object it's really fast.
These are my two cents.By the way, I added some flag to this topic, hope this will help to get more comments from other guys here.
 
Yehonathan Sharvit
Author
Posts: 20
5
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you for your feedback.
The main idea of DOP is to work with immutable data almost everywhere.

And it seems that Java embraces this idea by adding records to Java 14.

The conventional way of doing DOP in Java would be to use records (or lombok @data annotations).
The less conventional way would be to use hash maps almost everywhere and get rid of objects.

I'd really like to hear thoughts from Java experts regarding the conventional and the less conventional way of embracing DOP in Java.
 
Marshal
Posts: 72479
315
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Maps don't get rid of objects; they store them (or more precisely their references) in such a way that they can easily be retrieved.
 
Yehonathan Sharvit
Author
Posts: 20
5
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
When I say "get rid of objects", I mean representing a data entity like a person with two data fields name and an age as a Map<String, Object>.
 
Campbell Ritchie
Marshal
Posts: 72479
315
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
...and what happens when you have two people with the same name and different ags?
 
Yehonathan Sharvit
Author
Posts: 20
5
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You store each person in a separate maps.

var joe = Map.of("Joe", 42);
var kelly = Map.of("Kelly", 37);
 
Campbell Ritchie
Marshal
Posts: 72479
315
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
What about type‑safety? If everything is a Map<String, Object>, you cannot rely on your object being the correct type. What you showed looks more like a Tuple (=Pair, =Duple) than a Map to me.
You have already suggested a much better feature suitable for that sort of objects: records.
 
Claude Moore
Bartender
Posts: 1304
39
IBM DB2 Netbeans IDE Spring Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:You have already suggested a much better feature suitable for that sort of objects: records.


...and records are classes without boilerplate code, OOP principles aren't thrown away
 
Campbell Ritchie
Marshal
Posts: 72479
315
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
That's one of the reasons why records are so much better. Their immutability is another advantage.
 
Yehonathan Sharvit
Author
Posts: 20
5
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
In Data-Oriented programming, we trade off type-safety for flexibility.

I know it's not natural in Java. That's why I asked for advices from open-minded Java experts.



For information systems that deal with information of dynamic nature, this tradeoff might make sense in some parts of the coded, even if it's not natural for Java developers.


Suppose you need to represent the data of a JIRA ticket with fields like: title, author, priority etc.. in addition to custom fields that are defined by the user. How would you store those fields as members of a class whose layout is defined at compile time in Java?
 
Claude Moore
Bartender
Posts: 1304
39
IBM DB2 Netbeans IDE Spring Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Yehonathan Sharvit wrote:

For information systems that deal with information of dynamic nature, this tradeoff might make sense in some parts of the coded, even if it's not natural for Java developers.
Suppose you need to represent the data of a JIRA ticket with fields like: title, author, priority etc.. in addition to custom fields that are defined by the user. How would you store those fields as members of a class whose layout is defined at compile time in Java?



Good question. I would say that if the custom fields are nothing more that a list of couples <attributeKey, attributeValue>,  where both the key and the value are arbitrary (i.e, there are no constraints limiting how many attributes an user can add to the ticket) we could code a class like this:



without using a map  
To be honest, I admit that using a list of record or, more simply, an HashMap shouldn't make a big difference at a practical level. At the very end, one should write helper method to find an attribute, get an attribute value and so on.






 
Yehonathan Sharvit
Author
Posts: 20
5
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
In fact your list of TicketAttribute is inline with the Data-Oriented spirit.

The Data-Oriented approach would treat regular attributes and custom attributes in the same dynamic way.
The benefits is that it gives you a unified approach to data.

Let me give you a couple of benefits of this unified approach to data:

  • Get an attribute with myJiraTicket.get("myCustomAttribute") without the need to know if it's a custom attribute or a regular attribute.
  • Count the number of attributes without any custom codde
  • Serialize a JIRA ticket to JSON without any custom code
  • Duplicate a JIRA ticket to JSON without any custom code



  • I am not saying that you should this approach everywhere in Java. I am trying to illustrate what are the benefits of this approach in order to discover together what are the situations and use cases where it makes sense to trade off type safety for data access flexibility.


     
    Claude Moore
    Bartender
    Posts: 1304
    39
    IBM DB2 Netbeans IDE Spring Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Yehonathan Sharvit wrote:
    Let me give you a couple of benefits of this unified approach to data:

  • Get an attribute with myJiraTicket.get("myCustomAttribute") without the need to know if it's a custom attribute or a regular attribute.


  • But this way you cease to treat a JiraTicket as a well-known structured type.


  • Count the number of attributes without any custom codde


  • That's true.


  • Serialize a JIRA ticket to JSON without any custom code
  • Duplicate a JIRA ticket to JSON without any custom code



  • Serializing  to Json in Java it's trivial (using Gson or Jackson library), I don't see a really advantage.


    I am not saying that you should this approach everywhere in Java. I am trying to illustrate what are the benefits of this approach in order to discover together what are the situations and use cases where it makes sense to trade off type safety for data access flexibility.



    In my very humble opinion: I can imagine that using an hashmap as generic container could be really useful only when you need to deal with a very unstructured scenario, for example to fetch data from a datasource, and then fetch the map in some kind of structure DTO. This way you could easily create a layer that encapsulates all the machinery needed to deal with JDBC at a very low level.
    In my experience I used an HashMap of parameters to pass data from and to remote method of EJBs, but in hindsight, I admit it was a poor design.

    I don't mean that Data-Oriented programming style is bad. Simply. it may not cope well with java.
    Think about python: you could write something like this



    i.e, you can define attributes on the fly and then accessing them in a simply notation (object dot attributename). That's not the case of java, and accessing an hashmap via get(key) and set(key, value) it's - at least for me - definetly ugly.


     
    Yehonathan Sharvit
    Author
    Posts: 20
    5
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Ok. Let's say that  for now, having a flexible access to data is not beneficial enough to a Java developer so that he is willing to trade off type safety for it.


    When about the other approach presented in the article where:

  • Data is represented with Java 14 records
  • Code is stored in static methods


  • Do you guys think that it could help reducing the complexity of  Java programs?
     
    Claude Moore
    Bartender
    Posts: 1304
    39
    IBM DB2 Netbeans IDE Spring Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Yehonathan Sharvit wrote:

  • Data is represented with Java 14 records



  • Could be a nice approach. The only drawback i can see with records is that they're immutable.
    I can't understand - my limit - why. Quite sure I'm wrong, but a C struct-like behaviour of record IMHO would have been better. When you read data from somewhere, you'll want to manipulate them.
    Being record immutable, you'll end to write helper methods to accomplish a simple task - change a value.

    Yehonathan Sharvit wrote:

  • Code is stored in static methods



  • That's a point i did not understand. Could you elaborate further ?
     
    Campbell Ritchie
    Marshal
    Posts: 72479
    315
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Claude Moore wrote:. . . . The only drawback i can see with records is that they're immutable. . . . . When you read data from somewhere, you'll want to manipulate them. . . . helper methods to accomplish a simple task - change a value. . . .

    Immutable objects have all sorts of advantages, including threa‍d‑safety. It is easy enough to change data, but you need a new instance:-That is how you alter data in any immutable kind of object. Maybe in the good old days of JDK1.0.2 and big PCs having 16MB of RAM, it would be a performance bottleneck or memory hog, but those days are long past.
     
    Claude Moore
    Bartender
    Posts: 1304
    39
    IBM DB2 Netbeans IDE Spring Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Thanks Campbell.


    Immutable objects have all sorts of advantages



    It would be great if you could share a link where I can learn about it.
     
    Campbell Ritchie
    Marshal
    Posts: 72479
    315
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    There's a section about it in Joshua Bloch's Effective Java. 2nd edition=page 73, 3rd edition=page 80. You will see that the technique of returning new Child(this.name, this.age + 1) is a very much over‑simplified version of how String#toLowerCase() and BigDecimal.multiply() work.
     
    Yehonathan Sharvit
    Author
    Posts: 20
    5
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Making sure we never mutate data is an essential part of Data-Oriented programming.

    Now, regarding static methods:

    Traditionally in OOP, we encapsulate data in objects that contain both members and methods.

    When data is represented as records we split code and data in two different entities:

    1. A record for the data
    2. A class with static methods for the code, where the methods receive the data they manipulate (the state) as an argument.

    The main benefit of this approach is that it reduces the complexity of the class hierarchies.


    I have made this provocative diagram to illustrate this complexity reduction.



    Let me know if it makes things a bit clearer.


     
    Campbell Ritchie
    Marshal
    Posts: 72479
    315
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Yes, it does. The left side looks like the sort of inheritance hierarchies you saw in the older books. Fortunately in the last few decades people have realised that lots of inheritance often introduces new problems and inheritance is used much less nowadays.
    Let's see what we can write, assuming I have the Child record from my post this morning, minus the celebrate birthday method.That would work, but you now have coupling between the two classes. It is like what Claude Moore said earlier: a C struct and functions to operate it.
     
    Campbell Ritchie
    Marshal
    Posts: 72479
    315
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    But the inheritance hierarchy of records is simpler: they all extend Record and don't have any subclasses.
     
    Master Rancher
    Posts: 3831
    50
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Campbell, I don't think any of the relations in that diagram are inheritance.  It uses Class diagram symbols -  I see mostly composition, association, and dependency.  Nothing implying one class extends or implements another.

    To be fair, I think that on the right, each of the Code classes should also depend on one or more Data classes.  CatalogCode needs to know about CatalogData at least, no?  Changes to CatalogData would probably affect CatalogCode.   If we're showing dependencies on the left side, it seems unfair to leave them out on the right.

    I do see some appeal to this approach, especially with Java records.  
     
    Claude Moore
    Bartender
    Posts: 1304
    39
    IBM DB2 Netbeans IDE Spring Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Campbell Ritchie wrote:That would work, but you now have coupling between the two classes. It is like what Claude Moore said earlier: a C struct and functions to operate it.



    But at this point I wonder if it's really necessary to have a RecordXYZUtils class for each RecordXYZ type. Wouldn't be easier to code all static methods needed to manipulate a Record class within the Record class itself ? PRO: self contained compiling units; CON: your record class isn't lightweighted anymore.
     
    Campbell Ritchie
    Marshal
    Posts: 72479
    315
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Mike Simmons wrote:Campbell, I don't think any of the relations in that diagram are inheritance. . . .

    Do you think I'd have noticed that if I'd looked at the diagram properly?
     
    Mike Simmons
    Master Rancher
    Posts: 3831
    50
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Yehonathan Sharvit wrote:Ok. Let's say that  for now, having a flexible access to data is not beneficial enough to a Java developer so that he is willing to trade off type safety for it.



    I've been thinking about this one.  For me, it's not that I'm completely unwilling to move away from type safety in favor of flexibility, for some application where that may be beneficial.  But it's also that Java itself makes doing that painfully verbose, having to keep casting things everywhere.  I appreciate the effort you put into presenting ways to achieve that, but the result still feels very cumbersome in Java.  I feel that if I want to use that sort of approach, I'd probably not want to use Java.  Perhaps Groovy would be a way to move more smoothly between typed and untyped code?  I'm not sure.  

    I do think that future Java enhancements might make this approach more feasible - the recent changes where instanceof introduces an implicit cast to the subsequent code, that seems like it could help a bit in handling untyped data.  And more should be coming in upcoming JDK releases - they're moving towards an enhanced switch statment that works much like scala's pattern matching.  That could make it somewhat nicer to both test an element type and then handle it appropriately.  Also giving you more control over what to do if an element isn't the type you think it is, in case ClassCastException is not the desired approach in some cases.

    Yehonathan Sharvit wrote:When about the other approach presented in the article where:

  • Data is represented with Java 14 records
  • Code is stored in static methods


  • Do you guys think that it could help reducing the complexity of  Java programs?



    This does have some appeal.  Given that records are "official" now (not a preview version), I'm eager to use them to present concise, clean representations of data, without the clutter.  I'm fine with moving the methods elsewhere.  You may see some resistance to the idea of all methods being static though... mostly from a testability perspective.  If a complex method relies on several other complex methods to function, we often want to mock out the other methods to better test one method at a time.  So you may want to not insist on methods being static necessarily.  They can be instance methods on a singleton-like object that can be mocked out.  That may be more agreeable to many Java developers.

    Than you for bringing the topic up here; I'll continue to think about it.
     
    Claude Moore
    Bartender
    Posts: 1304
    39
    IBM DB2 Netbeans IDE Spring Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Mike Simmons wrote:
    This does have some appeal.  Given that records are "official" now (not a preview version), I'm eager to use them to present concise, clean representations of data, without the clutter.  I'm fine with moving the methods elsewhere.  You may see some resistance to the idea of all methods being static though... mostly from a testability perspective.  If a complex method relies on several other complex methods to function, we often want to mock out the other methods to better test one method at a time.  So you may want to not insist on methods being static necessarily.  They can be instance methods on a singleton-like object that can be mocked out.  That may be more agreeable to many Java developers.



    Seems perfectly reasonable to me.
     
    Yehonathan Sharvit
    Author
    Posts: 20
    5
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Based on your reactions in this thread, I wrote a blog post to illustrate various ways of providing dynamic data access in Java.

    Looking forward to get your feedback.
     
    Claude Moore
    Bartender
    Posts: 1304
    39
    IBM DB2 Netbeans IDE Spring Java
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Yehonathan Sharvit wrote:Based on your reactions in this thread, I wrote a blog post to illustrate various ways of providing dynamic data access in Java.

    Looking forward to get your feedback.



    There's another topic, related to Data-oriented programming, you should elaborate further: persistence.
    All your examples are related to accessing data once they're in memory, but any serious program needs to persist the data it handles. JPA (and Hibernate, the de facto standard implementation) cannot handle record directly as entities.
    So, if we need to use record not only to represent custom data types (like the forementioned C-struct like construct) or to use them as DTOs (data transfer objects), we need a way to work with JPA.
     
    Mike Simmons
    Master Rancher
    Posts: 3831
    50
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Yehonathan, thanks for the followup article.  I confess that for the first article, I didn't read as carefully once I saw the section with the nested casts ("((String)((Map<String,List>)((Map<String,Map>)") - I thought it looked to icky to contemplate much.    Now I see where you were going... much better.

    Your code inspired some revisions and additions on my part.

  • On many platforms (including my own MacBook Pro it seems), to access a private field with reflection you may also need a field.setAccessible(true).
  • Restrictions on reflective access have been tightened in JDK 16 - you might have to run using the flag --illegal-access=permit.
  • I hate checked exceptions and certainly don't want to deal with them every time I use a library like this.  Yeah, a lot of other Java programmers will disagree.  But face it, those people probably won't be willing to use this sort of approach anyway.    So, let's wrap the checked exceptions with some kind of RuntimeException.
  • While we're at it, if anything does go wrong, it would be really helpful to capture more diagnostic info in the error message.
  • We don't need to make users create a List every time they call this.  We can use varargs to make it easier to call the code.  This also eliminates the need for a single-argument overload.
  • We can use generics to allow the code to infer the type from the calling context.  You come close to this with your TypedGetter, but I think type inference is more flexible.
  • I don't find getInAs very clear as method names go - given that, we might as well shorten it to something minimal like get().  The class name will also provide context to readers - thus, Dynamic.get(obj, "field1", "field2") seems fairly concise overall to me.
  • If one of the elements in the path returns null, we can just return null for the whole expression, rather than throwing an NPE.  Not everyone will agree, but to me this seems like a sensible default.
  • I like having primitive converters that replace null will the default value - so getInt() returns 0 instead of null.  If the user doesn't want that, just ask for a wrapper instead.


  • E.g. for the last point, you can choose between

    and

    and

    The first allows n to be null.  The second converts null to 0.  The third throws a NullPointerException.  (And, on my IDE, correctly shows a warning for "Unboxing of 'Dynamic.get(obj, "n")' can throw a 'NullPointerException'.)  The programmer can choose which they want.
     
    Mike Simmons
    Master Rancher
    Posts: 3831
    50
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    So, here's what I have so far:

     
    Yehonathan Sharvit
    Author
    Posts: 20
    5
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I like what you created Mike! Curious to see how other Java folks receive it.

    Could you elaborate further on what you wrote about type inference and give an example?
     
    Campbell Ritchie
    Marshal
    Posts: 72479
    315
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Mike Simmons wrote:. . .

  • I hate checked exceptions . . . let's wrap the checked exceptions with some kind of RuntimeException.
  • . . .

    Checked exceptions are brilliant (‍) . . . until you stray the slightest way beyond the bounds of strict object‑oriented (=OO) programming. Functions can't cope with checked exceptions, which is why the Streams API (java.util.stream.Stream, etc.) wraps all their exceptions in unchecked exceptions. Classic unchecked example. Actually, functions shouldn't permit exceptions full stop. This means that Oracle aren't doing what it says about exceptions in the Java™ Tutorials.

  • . . . primitive converters that replace null will the default value - so getInt() returns 0 . . . .
  • What a nice idea. it looks very similar to OptionalXXX#orElse(). It also brings me out in spots. I have gradually developed an allergy to primitives in an OO language. Primitives are all right as long as you confien yourself to arithmetic etc., but as soon as you try anything more complicated, you have to use sentinel results. This is one of the oldest examples of a sentinel result, which probably predates Kernighan and Ritchie; at least in this instance you can be sure that the sentinel result can't be a “real” result, but zero/null char/false are perfectly acceptable “real” results in some circumstances.

    . . . The programmer can choose which they want.

    Another good feature
     
    Mike Simmons
    Master Rancher
    Posts: 3831
    50
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Yehonathan Sharvit wrote:Could you elaborate further on what you wrote about type inference and give an example?


    Sure.  I now see that my previous post had a sentence fragment I never finished... I had meant to elaborate in the first place.  (I've now edited the old post to remove that fragment.)  Anyway...

    The signature of my get() method uses type inference:

    The key here is, how is T determined?   The <T> means it's declared in this method, not a class parameter - so it has nothing to do with any T anywhere else in the class.  It's only used as a return type, and does not appear anywhere in the method arguments.  So when the compiler tries to resolve what type T is, it's determined only by the context where we use the return type.  As far as the compiler is concerned, it can be anything.  So we can write

    or

    or

    And those all work.  The compiler infers the return type from how we use the method.  And it silently converts these to bytecode equivalent to:

    But we don't need to actually write that - it's done for us.

    Now the downside here is, by using this style we're giving up one of the benefits of Java's strong typing.  The compiler is not telling us at compile time if we're making a mistake here - it will just let us assume any type, and then throw an error later if we're wrong.  That's somewhat antithetical to what we're used to in Java.  But in this case, we were doing that anyway... if we just returned Object, we'd be explicitly casting almost every time we use the method.  And the compiler would be fine with that, and then at run time we'd find out whether the cast was correct or not.  Some may feel there are benefits to being explicit about this sort of thing... but it's also more verbose.  And in many cases you can just make sure the type is explicit in a variable declaration.

    So basically we're replacing

    with

     
    Campbell Ritchie
    Marshal
    Posts: 72479
    315
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Very interesting point there. But isn't that the equivalent of using a raw type? If you made the parameter type T, that would very simply give you type‑safety back.
     
    Mike Simmons
    Master Rancher
    Posts: 3831
    50
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Campbell Ritchie wrote:Very interesting point there. But isn't that the equivalent of using a raw type?



    Similar, I suppose.  Except raw types give you warnings, and this doesn't.  And raw types exist in the context of a parameterized type.  This works for any type, parameterized or no.

    Campbell Ritchie wrote:If you made the parameter type T, that would very simply give you type‑safety back.



    Parameter type of what?  This is a static method, none of whose arguments have anything to do with T.  We could introduce another parameter of type T or type Class<T> - but then, you have to use that parameter to call the method.  Which seems like more work - the point was to streamline the code.

    Recall, the use case here is that we're dealing with either Map<String, Object>, or an arbitrary record class... or other arbitrary class.  And trying to extract a field by name and treat the value as being a particular type.  The compiler doesn't know what type will be associated with a given String key.  We have to tell it.  Whether we do that by explicitly casting, or using the return value in a place that expects a certain type, or by passing a Class<T> object, or something.  We're telling the compiler, hey, treat it as a T.  And the JVM will find out at runtime if that's correct or not.

    Another way to think of it is, we're effectively disabling type checking for this one method.  Because for this one method, we would otherwise need to cast it every time we use it.  This results in cleaner looking code.  And it means the programmer should not expect the compiler to tell them if they get the type wrong, for this method.  If you prefer to explicitly cast each time you use the get() method, well that's an option too, a more traditional Java-like way for us to write this library.  For myself, I'm trying to reduce the verbosity of using an untyped data structure within the type-strict world of Java.
     
    Campbell Ritchie
    Marshal
    Posts: 72479
    315
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I see what you mean. The bit about casts reminds me of programming JDK1.4 (and earlier).
     
    Claude Moore
    Bartender
    Posts: 1304
    39
    IBM DB2 Netbeans IDE Spring Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Mike, let me tell you that the code you wrote is beautiful. But I wonder, using your approach, you could write a utility class to "mappify" (converting to a map) an object  and vice versa.
    What would you think ?
     
    Mike Simmons
    Master Rancher
    Posts: 3831
    50
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Thanks, Claude.  Yes, I imagine code like this could be extended to some sort of "mapify" library, and could indeed be useful.  Then too, there are various libraries already out there, Dozer, Orika, JMapper, MapStruct, probably others, that seem to do that sort of thing - I haven't used any extensively for a while (though I liked Dozer years ago), but I'm wondering how friendly the APIs for those are nowadays.

    I was also thinking that this "path" approach reminds me of XPath.  It might be interesting to develop a tool that can apply an XPath to an arbitrary map-of-maps structure.  Which probably already exists to some extent, as things like XML and JSON are often parsed into map-of-maps structures, and I suspect something like JSONPath is doing this sort of thing already.  Maybe it can be adapted to also work with records?

    I've also been thinking about your comment regarding the mismatch between Hibernate/JPA and immutable records.  That's certainly an issue.  But I think it's not just for data-oriented programming, but more of a pan-Java thing.  Now with records coming out, I think we'll see more movement towards using immutable classes more and more, and there will be pressure for a new JPA version to more directly support conversion to and from records.  Maybe something that allows JPA-style annotations within a record declaration, such that JPA can construct an instance using the record constructor, rather than expecting a non-arg constructor and mutable fields?  I don't know; it's complicated.  But it will be awhile for people to come up with a good solution to that, I think.  Meanwhile, I'm thinking we're just going to have to convert between mutable JPA entity classes, and immutable records.  Which takes us back to needing a good mapper library to facilitate conversion with a minimum of pain.  Maybe that's the most profitable area to investigate here... do any of the mappers libraries currently handle records?  What's the best way to do that, I wonder?
     
    Oh the stink of it! Smell my tiny ad!
    SKIP - a book about connecting industrious people with elderly land owners
    https://coderanch.com/t/skip-book
    reply
      Bookmark Topic Watch Topic
    • New Topic