Re: 7.0 wishlist?



Andreas Leitgeb wrote:
Not sure, if you got that idea yourself, or
from reading <slrngfmc50.4jb.avl@xxxxxxxxxxxxxxxxxxxxxxxx>.
(in case google distorts it: "slrngfmc50.4jb.avl" is the part
before the "at"),
appears to be addressing me specifically.
The question was directed at you, indeed

I found the meta-discussion quite wearying. I'd wish we could
both just stick to discussing the ideas, and just ignore anything
meta.

Unfortunately, some people seem not only to be unwilling to do so, but all too willing to make public insinuations about other peoples' competence, honesty, or intelligence, which those people then naturally feel the need to publicly dispute in turn.

It would help if everyone tried to keep a level head and, however annoyed or frustrated they might become when someone persists in disagreeing with them about something, made an effort to bite back any insults they felt the urge to sling.

That includes subtle put-downs, insinuations, and general rudeness, condescension, and treating of someone as an idiot or a child, as well as explicit namecalling and accusation-leveling.

My proposal overloaded the "extends" keyword for this -- it would subclass a non-final class normally, as now, and wrap a final class. You appear to want to be able to wrap without subclassing non-final classes. For that a new syntax would be needed. Perhaps "implements" followed by a class rather than an interface would do the job without adding new reserved words to Java?

I now understand your proposal. Obviously (now) it is not the same as
mine.

Though it has overlaps.

Compared with mine, you left out the class-equivalence part.
Without that, I don't see too much point in the whole thing.

I fully intended the wrap to test true with "instance of" the base, be castable to the base, and so forth. You want two-way equivalence, which is fine for a separate "wraps" type syntax (but obviously not for "extends").

My approach of keeping them equivalent failed with generics erasure.

Yeah, you might have to cast it back to the wrapper type sometimes.

However, there are new problems I see with it (non-nullness) now:
Assume I wanted to use a HashMap<String,Integer*> I'd expect it
to only contain non-nulls, but get() would still produce nullable
values, as it returns null for unknown keys.

This remark was based on my misunderstanding. I thought compile-time
coverage, you think runtime checks.
I accept, but undervalue the merits of runtime-checks.
Otoh., doing it at compile time runs into the problems shown.

Compile-time checks can be a part of it -- determining where the run-time checks are unnecessary, and perhaps requiring an explicit cast in certain cases.

I'll just withdraw myself from this topic, until some new ideas come
up. Looking forward to the @NonNull annotation, though.

Annotation, schmannotation. I want it to be an integrated part of the type system, not a bag on the side. Annotations are verbose and might not be usable on generic type parameters.

Of course it's no magic bullet, but it would help to catch some null-related errors closer to where the real bug was.
Yes. I strive for little more magic, though :-)

You'll just have to settle for what's technically feasible, like all of us. :-)

There'd need to be a way to declare a type parameter nullable, say
public V? get (K key) {

The problem is compatibility: any already existing generic class
would not know about the necessity to thusly mark some of it's methods
and consequently make wrong promises as a result.

Well, java.util and other cases in the core APIs would be updated along with Java as a whole. Sources compiled as Java 6 or earlier would be assumed able to return null (so a get() in a generic MyMap in a third-party library somewhere would be assumed able to return null, even for a MyMap<Key, Value*>, until MyMap was compiled with Java 7).

Another option is to assume type parameter returns can be null unless marked with a ?, and then they may only be null if the type parameter is not non-nullable:

V get () -> may be null
V? get () -> may be null if <Key, Value>, but not if <Key, Value*>
V* get () -> may not be null
Object get () -> may be null
Object* get () -> may not be null

The question mark could be reversed, such that it would be necessary
to hold the notnull-info on return values.

Indeed. See above.

That would solve the compatibility problem at the cost of being even
more confusing.

I don't think it's that confusing. No more than generics already is, anyway. :-)

In that case we could use "*" also within generics, and only if
both a type-parameter is "*"ed (public V* get() ...) AND the type
parameter was "*"ed (List<Integer*>), then the return value would be
known as statically not-null.
All fine, except that I dislike the asterisk as marker.

Got another (equally brief) preference? The specific choice of asterisk (and question-mark) is negotiable. :-)

Requiring an explicit cast at a nullable->non-nullable assignment where the compiler cannot prove by static analysis that the RHS isn't null might be a good idea.
quux = (Object*)bar; // OK, but may throw if bar is null

I consider it a necessity. Except for the asterisk, I like your
example very much: either separate out the null with an "if"
(and deal with the null-case separately), or just cast and have
the NPE thrown eventually.

As long as the NPE is thrown by the cast, not somewhere downstream later on; the latter is going back to square 1 with null-safety.

Probably the asterisk is bothersome to C programmers who read the above as a cast of bar to pointer-to-Object. As I said, the specific symbol is negotiable, but it should be brief, preferably one not-allowed-in-identifiers punctuation character.

Concurrency is a beast. Unless we had special idioms that resulted
in: getField...,dup,isnull?,ifyes:NPE,else:putField (pseudo-bytecode),
we'd indeed still need the cast.

Nah, just put in the runtime check if it cannot be null without a concurrency bug, but might be with one. Concurrency bugs show up as odd effects in odd places (when they don't show up as deadlocks) anyway. At least an NPE in seemingly-null-safe could would then be prima facie evidence of thread-unsafety in the null's source chain, and with a runtime check on the reference assignment the NPE will occur in one of blocks that participated in a race condition instead of somewhere random later on.

Putting runtime checks for null at sites other than where they already occur and explicit casts could also be something done when compiling for debug and not when compiling for production, or something enabled at runtime. I'd suggest:

reference* = local; // No check without explicit cast. No cast needed
// if static analysis says local isn't null.
reference* = (Foo*)foo; // Run-time check.
reference* = foo*; // No check, no cast needed
reference* = foreign; // No cast needed if static analysis says foreign
// isn't null without concurrent modification.
// Run-time check though.

These producing, respectively, bytecode equivalent to the following Java 6 code snippets:

reference = local; // reference* = local;

reference = foo; // reference* = (Foo*)foo;
if (reference == null) throw new NullPointerException();

reference = foo; // reference* = foo*;

reference = foreign; // reference* = foreign;
assert reference != null;


Disabling assertions would mean an NPE at a downstream use of reference if foreign got nulled concurrently; enabled, an AssertionError at the point of assignment, right smack in the middle of one of the pieces of concurrent code that is involved in a race-condition bug.

This seems to me to strike the best balance between performance and safety/bug-detection; null is checked for where it really needs to be checked for anyway, and nulls that would be secondary to concurrency bugs at worst are no harder to track down than presently, and may become easier to do so when assertions are enabled for debugging.

Anyway it would have to create a copy, or nulls could be sneaked
in later through arrray, and appear in noNullsArray.

Ugh. Another reason to prefer using collections. And a reason not to allow Foo<Bar*> to be assignable without a cast to Foo<Bar> or vice-versa.

The need for dummy names at all is rather perverse, in catch clauses and in private nested classes of all stripes.
but rare and non-verbose enough to not matter all that much.

Rare? You obviously don't write a lot of action listeners or exception handlers, unless the former are shared among many components and the latter are always doing an e.printStackTrace(). :-)

Also, some IDEs and compiler options produce warnings on unused variables. My proposal would allow such warnings to be enabled and still have warning-free code in these cases, without doing additional dummy actions.
I don't use such IDEs but I'm fairly sure, they do never barf for
unused parameter names.

Barf, no, warn, yes, depending on settings.

What did you mean should actually happen to checked exceptions
thrown within run() (and not caught there)?
Same as now. Thread dies and a stack dump goes to System.err.
But that's not the point of checked exceptions.

That's what PRESENTLY happens if run() throws an exception.

When the literal is used as the RHS of an assignment (including as a method parameter or return), the elements would have to be castable to the type of the LHS of the assignment.
That doesn't work. The compiler may need to choose among a couple
of overloaded methods.

When it's ambiguous, it should use whichever is the best fit based on low-common-denominator. I don't find method overloads with arrays whose signatures differ only by array type to be very common anyway.

So if you passed {2, 3, 5, 7, 11, 13} to

void method (Number[] nums)
void method (Integer[] ints)
void method (String[] strings)

it would pick the middle one. Much like overload resolution if you pass the literal 2 to

void method (Number num)
void method (Integer i)
void method (String string)

right now.

Actually there is no such thing as an array in the constant pool.

That might be worth changing.

As near as I can tell, we don't actually have "true" array literals right now, just a funny syntax for initializing array-type variables. Adding "true" array literals would involve adding "true" array constants.

I wasn't proposing to provide more than just try { } continue { } catch { } finally { }.
I see too little value in that. Therefore I tried to extend it
such that it pays :-)

Even if the results are awful, and require adding reserved words to the language into the bargain? :-)

The polling methods that block are actually named "remove" rather than "poll", but they exist.

I surrender. I don't know what made me miss it.

I can now add that I've actually added ReferenceQueueListener and addReferenceQueueListener to my local utility classes. The tricky bit is ensuring that the queue itself will be garbage-collectible if no longer in outside use. For that, I have the thread hold the queue in a WeakReference<ReferenceQueue> (my, how the tables have turned, ReferenceQueue!) and if the remove(long timeout) method hits the timeout, it sleeps for a while holding no other references to the queue. If it's no longer in use (which presumably means no reference objects still out there that reference it, and no code that might produce more), it can go. When the thread comes out of sleep, it gets the queue back out of the WeakReference, and if it's null, it exits. So the thread dies. And the static map linking queues to threads (so removeReferenceQueueListener works, and so there can be one thread per reference queue with listeners instead of per listener) is a WeakHashMap so the entry there disappears. The whole affair seems to let everything involved be collected when not in use.

I also wrote a Cache class using a HashMap and using this to register a listener to remove mappings that are no longer in use. (It subclasses WeakReference to add a "key" field, so when the reference listener gets a reference it can remove the mapping.) I then used it in some existing code. It appears to work. When I put System.out.println("x") in a listener I got xs in the console when I dropped references to cached objects -- surprisingly promptly, even -- and caching and releasing a fair number of objects, some of them large, doesn't seem to bloat the process size, suggesting that it's not leaking.

Longer-term use will be needed to see if it definitely performs ideally, but that I knocked this up in less than three hours and had an immediate use for it makes me wonder why this functionality is missing from java.lang.ref.

But none of them (including even my WeakHashMap approach)
allow checking at compile time, as the proposed change would.

public final class Foo {
RealFoo delegate; // Package-private field. No subclassing.
Ah ok, dispersing a public final class's instance that keeps all its sensitive parts in the delegate does indeed solve it.
But it's ugh-lee ;-)

The technical term is "encapsulation". ;-)
.



Relevant Pages