• 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
  • Tim Cooke
  • Ron McLeod
  • paul wheaton
  • Jeanne Boyarsky
Sheriffs:
  • Paul Clapham
  • Devaka Cooray
Saloon Keepers:
  • Tim Holloway
  • Roland Mueller
  • Himai Minh
Bartenders:

Time to revisit the primitive immutable wrapper objects in the java.lang package?

 
Ranch Hand
Posts: 160
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Is it time to revisit the primitive wrapper objects in the java.lang package? I think Java's early attempts at object wrappers aren't ideal and lead to arguments about when to use objects vs primitives. Primitive types always win out over objects because of their speed, changeability, and lower memory requirements. Of course this argument misses the point entirely, especially because often people who choose primitive types are still using objects but just creating their own custom, mutable wrapper classes rather than the premade immutable ones.


Objects aren't that big a deal on system requirements on their own, objects that store state are just frames around one or more primitive types, arrays, and/or references to other wrapper classes, but rather the true issue is an argument over immutable vs mutable state in objects.

immutable - the state of this primitive cannot change its value once it's set. A new value is set by creating a new object with a different value assigned.

mutable - this value, in a wrapper object, can be set and re-set just like any primitive without much overhead (a set method).


Tell me if I get these things right:
These wrapper objects were originally designed to allow programmers to do fun things with primitives, such as add them to collections with the added benefit of search and placement, or use the many utility/type-conversion methods of these classes, but these objects in the java.lang package are also immutable and that causes problems:

1. The most used wrapper class, as example: String isn't a primitive type at all in Java, but String is one of the bundled object wrappers, an immutable char array to be exact, and I have heard the term "String-pool" used -- or, in other words, Java preserves all the String objects in memory, while Java's VM runs, in case values of those String objects are reused.

2. Other wrapper classes, in java.lang, are also immutable, I'm not sure this is how Java works, but it sounds like Java is just using the same "Object-pool" cache model for all immutable objects? Thus, giving objects a bad reputation as memory hogs -- because you can't just change them on the fly, but Java needs to create a new object with each change and store the previous values in case you'll use them again.


Is the final-only wrappers just a mistake of the original API designers that has been overlooked over the many later Java versions?

I have noticed that a lot of API library growth seems to have been done to try and compensate for the bad object vs primitive, actually immutable vs mutable, model Java originally started with, and to try and encourage developers to use the new premade objects rather than build their own types from scratch.

1. Take the collection classes. Originally, they only took type Object (Vector, HashTable, Stack ...), and you would wrap up primitive values in using the immutable wrapper classes of java.lang then unwrap them when you wanted to use them. In later Java versions, collection type safety was added, example: "<String>", and this also allows you to specify your own custom classes, but is this just an admition that Java doesn't have the wrapper classes that you want?

2. Also, similarly, the whole java.util.concurrent package is like an object version remake of the low-level, primitive-like, wait-notify model. Methods: wait() and notify() could only work in synchronized methods leaving the consumer-producer model in the same place as primitive modification, but the concurrent package takes the consumer-producer model to a new level: it integrates objects into a model of Queues and thread-safe collections.


When you take away the API, Java really comes down to a class that uses the following primitive fields to track its state:

boolean, byte, short, int, long, float, double, char. +array, +object reference (such as String).

So, that leads me back to my question: Is it time to revisit the primitive wrapper objects in the java.lang package? Such as to add mutability to wrapper objects and, perhaps, additional primitive-array combo-types than just char[]-as-String? Actually, I mean the official Java API developers at Oracle?
 
Sheriff
Posts: 3837
66
Netbeans IDE Oracle Firefox Browser
  • Likes 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I could not disagree more with your view. But let's take the questions first:

Wrapper classes were indeed created to allow using primitive types as objects, most importantly with collections. That much is true.

The "String pool" initially contains only compile-time constants. Adding other Strings into this pool is not done automatically, but by invoking the intern() method. String class provides various optimizations (eg. when creating substrings) and if you don't follow good practices with Strings, you can indeed increase your memory usage significantly. However, mutable versions of Strings do exist (StringBuilder and StringBuffer), so immutability of Strings is not actually an issue.

Other wrappers of primitives (Integer, Long and the like) also contain a small pool of often used values, but, unlike Strings, this pool cannot be extended at runtime and is really, really tiny. Immutability of these classes actually is not an issue, as the primitive types are mutable and intensive calculations should always be performed with primitives, avoiding excessive object creations.

Generics were not added into Java language to "compensate for the bad object vs primitive, actually immutable vs mutable", but to provide type safety while using collections. Type safety is great achievement in its own right and I honestly don't see how this could be interpreted as "just an admition that Java doesn't have the wrapper classes that you want".

I didn't get your note regarding the java.util.concurrent package, but generally speaking, this package addresses concurrency issues, not immutability issues, and addresses them very well.

I also don't quite agree with your claim that people are often creating their own custom, mutable wrapper classes for primitives. I neither ever created, nor needed one. I would actually consider a mutable primitive wrapper an indication of suspicious design.

On the other hand, I see serious problems with mutable wrappers:

1) Immutability is very valuable concept in my opinion. Among other things, immutability simplifies multithreaded programming tremendously. It also abolishes the need for defensive copying. This can more than compensate any increased memory usage, depending on the application. For example, I had much trouble with Java's Date, as it is mutable and I had to make defensive copy at so many places. It bothered me so much that I looked around and found the JodaTime library, with its immutable classes, and merrily use them ever since. (Mutable versions are provided by JodaTime too and I once in a while do use them.) I was actually on the verge of creating immutable wrapper class for the mutable Date.

2) You could not effectively pass mutable wrappers to methods by value. Mutability would effectively turn all these arguments into references, a "feature" which certainly would be widely abused by Java programmers.

3) Lots of Collection framework's classes would stop working. If you use mutable object as a key in a map, for example, and change it after inserting it into the map, you break the map internal structures and the map might not be able to retrieve the key (this is actually documented in the Map interface documentation, see also Set). To make these classes honor their contracts with casual usage of primitive, mutable wrappers, defensive copies of all inserted object would have to be made (either by the class itself, or by the users), which would incur significant performance penalty.

In short, I don't believe this is going to happen, but if it were, I'd have to move away from Java.
 
Walter Gabrielsen Iii
Ranch Hand
Posts: 160
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'm just adding a note of clarity involving the concurrency package. I read this early Java version tutorial: The notify() and wait() Methods part of Synchronizing Threads.

From the tutorial you can see what I mean, the way Java handled the consumer-producer pattern before the concurrency package existed, make a calling thread wait until a primitive value, a flag, tests true:


(Yes, I know this code looks scary, like a deadlock, but it turns out wait() isn't Thread.sleep(). The wait() causes the calling thread to release its lock on this object's monitor, while sleep() doesn't release its lock.)

And, the concurrency package is full of classes that contain many patterns such as this one, only improved, plus many more, that were once built from scratch (would you consider these new concurrent classes to be wrapper objects?).
 
Java Cowboy
Posts: 16084
88
Android Scala IntelliJ IDE Spring Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Martin explained it very well. The fact that String and the wrapper classes are immutable and final is a good thing and these classes were certainly designed like that on purpose; it was not an oversight in the beginning.

The String pool is possible exactly because strings are immutable. If strings were not immutable, you couldn't share string objects in a pool, because if you would change the content of a string in the pool, the change would be visible in all places where the string is used, which would be undesirable.

Note that primitive types is an example of a leaky abstraction. At a low level, a computer only works on bits, bytes, integers, and most modern CPUs also have hardware support for floating-point numbers. Many programming languages, including Java, expose these low-level concepts directly to the programmer as primitive types.

Other, more modern programming languages (for example Scala and Ruby) have no special primitive types; things like Int and Float are objects, which you can call methods on etc. just like on any other object. The compiler can figure out automatically that these things should be handled with the CPU's hardware support for these types. It's not necessary for the programmer to explicitly see the distinction between primitive types and non-primitive types - the compiler can hide all that.

If Java would be re-invented from the ground up today, then it probably should not have explicit primitive types.
 
Martin Vashko
Sheriff
Posts: 3837
66
Netbeans IDE Oracle Firefox Browser
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Walter Gabrielsen Iii wrote:From the tutorial you can see what I mean, the way Java handled the consumer-producer pattern before the concurrency package existed, make a calling thread wait until a primitive value, a flag, tests true:


But that code would look and work exactly the same with mutable wrapper classes. It has nothing to do with mutable/immutable concept - it just waits for a flag.

And, the concurrency package is full of classes that contain many patterns such as this one, only improved, plus many more, that were once built from scratch (would you consider these new concurrent classes to be wrapper objects?).


No, these classes definitely are not wrappers for primitives. Most of these are rich in functionality (synchronized queues, concurrent maps and so on). The few "mutable primitive wrappers" (AtomicInteger etc.) are actually designed not to provide mutability, but to provide atomicity. The atomicity allows their usage by different threads, and this sort of requires mutability, otherwise the different threads would not be able to see the same value. Synchronized queue used by two different threads is also "mutable" and it is so for the same reason (sharing data among threads).
 
Marshal
Posts: 80775
489
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jesper de Jong wrote: . . . Other, more modern programming languages (for example Scala and Ruby) have no special primitive types; things like Int and Float are objects, . . .

It's not only newer languages which have no primitives. Eiffel, which dates from about 1985, has INTEGER etc classes. And to make things more interesting, the more recent versions of C# have changed their primitives to objects. I don't know what that does for backward compatibility, however.
 
Campbell Ritchie
Marshal
Posts: 80775
489
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Walter Gabrielsen Iii wrote: . . .. . .

Did somebody in a tutorial really write == false? You doubtless already know not to use such a construct.
 
Sheriff
Posts: 22855
132
Eclipse IDE Spring Chrome Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Martin Vajsar wrote:I also don't quite agree with your claim that people are often creating their own custom, mutable wrapper classes for primitives. I neither ever created, nor needed one.


I have, for use as Map values that get increased quite a bit. Instead of getting a Map value, unboxing it, increasing it, boxing it again, putting it back in the Map, you can have one single object that you get, increase, and be done with it. Apache Commons even has a package with these classes, org.apache.commons.lang3.mutable.
 
Martin Vashko
Sheriff
Posts: 3837
66
Netbeans IDE Oracle Firefox Browser
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Rob Spoor wrote:

Martin Vajsar wrote:I also don't quite agree with your claim that people are often creating their own custom, mutable wrapper classes for primitives. I neither ever created, nor needed one.


I have, for use as Map values that get increased quite a bit. Instead of getting a Map value, unboxing it, increasing it, boxing it again, putting it back in the Map, you can have one single object that you get, increase, and be done with it. Apache Commons even has a package with these classes, org.apache.commons.lang3.mutable.


You're right, this is definitely a valid usage for such a class. Cannot understand how I've overlooked this quite obvious usage This is exactly the kind of mutable wrapper class Walter has been writing about.
 
Walter Gabrielsen Iii
Ranch Hand
Posts: 160
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:Did somebody in a tutorial really write == false? You doubtless already know not to use such a construct.



Wrong? not if you refer to the == vs .equals() difference since while is working on a primitive value, or if you refer to the value "false" it's unimportant here because the while loop is testing for an equality condition not the actual value involved. Here is the full class, I had to track it down in another page, Monitors, of that tutorial:
 
Bartender
Posts: 4568
9
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Campbell means that it's better style to use if (!available) and if (available) rather than if (available == false) and if (available == true). Partly because of the readability, and partly to protect yourself from the error of accidentally using = instead of ==.

(Probably )
 
Campbell Ritchie
Marshal
Posts: 80775
489
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Matthew Brown wrote: . . . (Probably )

That should be spelt d-e-f-i-n-i-t-e-l-y
 
Walter Gabrielsen Iii
Ranch Hand
Posts: 160
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I don't think it's better style, there in the above code example, to use a "!" (Logical complement) rather than an equality test, though in other code when working with objects and the equals() you're stuck with "!", but it doesn't improve my readability of the code here, it's just a different style.

Actually, I think (!available) is the scary version since the example is using primitive boolean types: "!", unlike the similar "!=" (not equal to) operator, isn't an equality test at all its a Unary operator for inverting the value of a boolean, in the same operator group as ++ or --.

The only reason "!" might work here is because the logic of the while loop somehow works backwards and still functions: first check that something is not the case and only then can the not-case be reason to test true and continue.
 
Campbell Ritchie
Marshal
Posts: 80775
489
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
It certainly is better style. And the bang operator isn't logical complement. You should not be using equality tests with the Boolean literals at all. You may sometimes require equality tests with booleans, eg if (b1 == b2 || b3 != b4) ...
The exclusive or operator is an alternative to != in that instance, but you need to remember its precedences.
 
Campbell Ritchie
Marshal
Posts: 80775
489
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Walter Gabrielsen Iii wrote: . . . The only reason "!" might work . . .

The only reason ! works there is because that is how it is designed to work
 
Walter Gabrielsen Iii
Ranch Hand
Posts: 160
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
With '!' , you're just inverting the boolean value before you test it. Then you run your condition, let's say the value is true, to run when it sees false, and, in the other case, when the value is false the condition runs when it sees true.

 
Walter Gabrielsen Iii
Ranch Hand
Posts: 160
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Why should you not use equality tests with booleans? I think boolean in Java is a psudo-type, unlike the +1/-1 contrast in some other languages, accessed through the keywords true and false.
 
Sheriff
Posts: 28416
102
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Walter Gabrielsen Iii wrote:Why should you not use equality tests with booleans?


Already answered by Matthew Brown a few posts back... because it's easy to confuse an equality test with an assignment.

I think boolean in Java is a psudo-type, unlike the +1/-1 contrast in some other languages, accessed through the keywords true and false.


What's a "pseudo-type"? I'd say that mapping boolean to a pair of integers could be described as "pseudo", but Java doesn't do that -- but anyway "pseudo-type" doesn't seem to be a computer science term, so there's really no point in us inventing meanings for it and arguing about those meanings.
 
Martin Vashko
Sheriff
Posts: 3837
66
Netbeans IDE Oracle Firefox Browser
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Walter Gabrielsen Iii wrote:With '!' , you're just inverting the boolean value before you test it. Then you run your condition, let's say the value is true, to run when it sees false, and, in the other case, when the value is false the condition runs when it sees true.


The ! is a boolean operator. It is not different from &&, || or ==, because all these operators produce a boolean expression. The while statement just expects a boolean expression. There is conceptually nothing like "inverting before testing", just boolean expressions being evaluated by if, for and while statements.

On the other hand, the opposition against equality testing with true/false has a rational reason that has already been mentioned above.
 
Master Rancher
Posts: 5177
83
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Martin Vajsar wrote:There is conceptually nothing like "inverting before testing",


Hunh? Sure there is. The ! operator inverts the boolean value before it's tested in the while statement.

Now, I have no idea why this should be considered objectionable by Walter. I'm just saying the term makes sense to me, not the argument.
 
Martin Vashko
Sheriff
Posts: 3837
66
Netbeans IDE Oracle Firefox Browser
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mike Simmons wrote:

Martin Vajsar wrote:There is conceptually nothing like "inverting before testing",


Hunh? Sure there is. The ! operator inverts the boolean value before it's tested in the while statement.


I probably didn't express myself very clearly. I meant that such concept does not exist in the language specification. I'd say it is better to think in terms of boolean expressions, which undoubtedly is how the while statement is defined in JLS. If someone gets too used to the idea that the ! operators inverts the test of the while statement, parsing a statement like while (!a && b) might suddenly be less straightforward than it need be.

(Years ago I've programmed in Pascal, which had a loop construct with condition at the end: repeat ... until expression. The loop finished when the value of the expression became true. This might sound logical to most people, but I'm not a native English speaker and I had serious problems coming to terms with this construct. I had spent ages staring at these conditions, mentally evaluating them only to find out I had forgotten to apply the crucial not. To thing of the ! operator as of something that inverts the while loop reminded me of the unfortunate repeat until. Just to explain myself Oh, the literal meaning of until in my language I had been repeating over and over again to me as an aid has suddenly come to my mind, still after all these years. Incredible.)
 
Mike Simmons
Master Rancher
Posts: 5177
83
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Martin Vajsar wrote:I meant that such concept does not exist in the language specification. I'd say it is better to think in terms of boolean expressions, which undoubtedly is how the while statement is defined in JLS.


Well, the JLS refers to the ! as the logical complement operator - which is how Walter referred to it, only to be told that "the bang operator isn't logical complement". There's often more than one way to describe a given concept, and I'm not sure how useful it is to try to get everyone to use the exact same way of describing it.

Martin Vajsar wrote:If someone gets too used to the idea that the ! operators inverts the test of the while statement, parsing a statement like while (!a && b) might suddenly be less straightforward than it need be.


But if they get used to the idea that the ! inverts whatever expression it's in front of, that seems fine to me. And referring to the boolean expression inside a while statement as a "test" seems OK to me too.
 
Campbell Ritchie
Marshal
Posts: 80775
489
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Walter Gabrielsen Iii wrote: . . . unlike the +1/-1 contrast in some other languages, accessed through the keywords true and false.

  • What +1/-1 contrast? I know of no language which uses +1/-1. I know of a lot which use 1 and 0, or -1 and 0, for true and false respectively, however.
  • And using -1/0 or 1/0 is much more error-prone than having separate boolean types.
  • Agreed, the boolean data type probably does store 1/0 or -1/0 somewhere, but the language specification wisely keeps such details safely hidden where we can't interfere with them and mess them up
     
    Walter Gabrielsen Iii
    Ranch Hand
    Posts: 160
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Campbell Ritchie wrote:

    Walter Gabrielsen Iii wrote: . . . unlike the +1/-1 contrast in some other languages, accessed through the keywords true and false.

  • What +1/-1 contrast? I know of no language which uses +1/-1. I know of a lot which use 1 and 0, or -1 and 0, for true and false respectively, however.
  • And using -1/0 or 1/0 is much more error-prone than having separate boolean types.
  • Agreed, the boolean data type probably does store 1/0 or -1/0 somewhere, but the language specification wisely keeps such details safely hidden where we can't interfere with them and mess them up



    I don't know what language uses -1 as false. I just assume that when I see C/C++ code that uses negative numbers to set a false state and positive numbers to set a true state.
     
    Walter Gabrielsen Iii
    Ranch Hand
    Posts: 160
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Oh, I think I should clarify something for anyone who is learning the concurrency package, the CubbyHole class above, posted earlier, which is an example of Java's wait()/notify() model, this model can also be implemented with the concurrent package, specifically the java.util.concurrent.locks.Condition interface and subclasses: await()/signal() model.
     
    Paul Clapham
    Sheriff
    Posts: 28416
    102
    Eclipse IDE Firefox Browser MySQL Database
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Walter Gabrielsen Iii wrote:Oh, I think I should clarify something for anyone who is learning the concurrency package, the CubbyHole class above, posted earlier, which is an example of Java's wait()/notify() model, this model can also be implemented with the concurrent package, specifically the java.util.concurrent.locks.Condition interface and subclasses: await()/signal() model.



    This is a good point. In fact, almost any code which you planned to write using wait() and notify() to synchronize multiple threads can be abstracted to use some classes from the java.util.concurrent packages, and probably should be. Concurrent programming is hard, so using components written by competent people is more likely to result in correct code than writing your own from scratch.

    Which brings us back to "revisiting" things... this means that wait() and notify() could be removed from "Java, the Next Generation" and people could be made to use the higher-level components.
     
    Campbell Ritchie
    Marshal
    Posts: 80775
    489
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Walter Gabrielsen Iii wrote:. . . I don't know what language uses -1 as false. . . .

    Nor do it. I know some which use -1 for TRUE, however. Because you can use a bitwise complement operator to turn it into FALSE, which is 0. I never knew C/C++ used negative for FALSE; I thought it was 0 for FALSE and non-zero for TRUE.
     
    Consider Paul's rocket mass heater.
    reply
      Bookmark Topic Watch Topic
    • New Topic