Andreas said:
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.
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.
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.
Yes. I strive for little more magic, though
You'll just have to settle for what's technically feasible, like all of
us.
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.
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
utField (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 said:
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().
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.
But that's not the point of checked exceptions.
That's what PRESENTLY happens if run() throws an exception.
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 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?
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.
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". ;-)