HashMap get/put

M

Mike Schilling

Peter said:
I'm afraid I don't understand how that can be. If you have a type
known at compile time to have a final method, how can that type be
substituted later on with a version in which that method is not
final? Do you have to do something with a special class loader? Or
is there actually language (as opposed to framework) support for
doing that?

The called class might haver changed at run-time; such a change
doesn't require that the caller be recompiled.
 
M

Mike Schilling

Peter said:
That seems like a serious versioning problem. What happens if you
have a class where the method is changed from non-final to final?
Does code compiled with that class previous still get to override
the
now-final method?

I think it'll fail to load, but I wouldn't swear to it.
 
E

Eric Sosman

Peter said:
Mike said:
[...]
The called class might haver changed at run-time; such a change
doesn't require that the caller be recompiled.

That seems like a serious versioning problem.

What happens if you have
a class where the method is changed from non-final to final? Does code
compiled with that class previous still get to override the now-final
method?

No, of course not. Similarly, if class A uses a public method
of class B, and then a new class B changes the method to private,
the already-compiled A can no longer use B's method.
I suppose that's one argument in favor of making all methods use virtual
dispatch, final or not. Then the method call always works the same
regardless. But it does seem to mean that if the contract for the class
changes, older code may wind up violating the new contract.

Yes, although it's not at all clear which party "violates" a
contract that's amended unilaterally.

The compiler will make sure that everything is kosher at the
time of compilation, but it cannot predict the future. The JVM,
looking at the actual .class files presented to it, is the final
arbiter of whether they'll play together or not.
 
M

Mike Schilling

Peter said:
I think I see the disconnect here. I'm talking about the eventual
native code being executed. When possible (i.e. in C# for methods
that aren't explicitly marked as "virtual"), static calls are
generated, and thus they are non-virtual. But yes, you're
right...that's not really accurate, since the language itself
doesn't
make the distinction in the _implementation_.

The C# compiler can create two completely different kiinds of code for
mwthods that are not declared virtual. Consider the following file:

namespace Test
{
interface IFace
{
void Doit();
}
class Virtual : IFace
{
public void Doit()
{
}
public void DoitNow()
{
}
}
}

DoitNow is generated as a non-virtual method. Doit is generated as a
virtual method that cannot be overridden. This is completely
transparent to the C# programmer, whether the creator or consumer of
these methods. Presumably it's done this way because a method needs
to be virtual to be part of an interface implementation.

The observstion I'd make here is that virtual vs. non-virtual is
really an implementation artifact. The semantic difference between a
non-virtual method and a virtual method that can't be overridden is
nil (or nearly so.)
 
W

Wojtek

Lew wrote :
But you cannot expect the compiler to catch all your mistakes. So this was
one that it couldn't catch because in their infinite wisdom the Powers That
Be decreed that 'Map#get()' not be type parameterized. A few minutes with
the 'Map' Javadocs before you refactored would've saved you some trouble.
Then you could've used Eclipse's Ctrl-Shift-G (find all references) or the
equivalent for your IDE and fixed the problem with even less effort than
letting the compiler find the error would have caused.

Which I did do (eventually). I was initially led astray by an
assumption about generics and how they applied to the Map class.

After all, when you define a (Map<K,V>) you must also instantiate it
I think we're spoiled by how much help Java gives us to catch and prevent
error. We start whining like little brats when we reach one of those corners
that we have to sweep with our own effort.

Hey look! No red markers! Yeah!
You did the community a good service by alerting us to this particular
corner.

<blush>
 
W

Wojtek

Lew wrote :
But you cannot expect the compiler to catch all your mistakes. So this was
one that it couldn't catch because in their infinite wisdom the Powers That
Be decreed that 'Map#get()' not be type parameterized. A few minutes with
the 'Map' Javadocs before you refactored would've saved you some trouble.
Then you could've used Eclipse's Ctrl-Shift-G (find all references) or the
equivalent for your IDE and fixed the problem with even less effort than
letting the compiler find the error would have caused.

Which I did do (eventually). I was initially led astray by an
assumption about generics and how they applied to the Map class.

After all, when you define a (Map<K,V>) you must also instantiate it
I think we're spoiled by how much help Java gives us to catch and prevent
error. We start whining like little brats when we reach one of those
corners that we have to sweep with our own effort.

Hey look! No red markers! Yeah!
You did the community a good service by alerting us to this particular
corner.

<blush>
 
M

Mike Schilling

Peter said:
I suppose that depends on what you're trying to talk about.

The fact that virtual vs. non-virtual isn't a very interesting distinction.
It's really about how calling mechanisms (prior to opimization, which may
make even that moot), not about semantics, because the semantics of "virtual
but final" and "non-virtual" are identical.
 
R

Robert Klemme

I don't understand. If the type is not the same as the type used in
the map there is no chance of success. Clearly this is an error. I'm
of the mind of the OP.

This is wrong: it solely depends on the implementation of equals() and
hashCode() whether the lookup key needs to be of the same class or not
and consequently whether a lookup can succeed or not.

If you want a practical example: assume a pair of classes MutableLong
and ImmutableLong (not in package java.lang). You implement hashCode()
and equals() in a way that instances with the same long value return the
same hash code and equals() also accepts instances of the other class.
Now you use a MutableLong for all sorts of operations including map
lookups but you use only immutable Long instances as Map keys in order
to avoid issues with key mutation which would make rehashing necessary.

Kind regards

robert
 
J

Jim Janney

Wojtek said:
Lew wrote :

Which I did do (eventually). I was initially led astray by an
assumption about generics and how they applied to the Map class.

After all, when you define a (Map<K,V>) you must also instantiate it


Hey look! No red markers! Yeah!


<blush>

The javadoc for java.util.Map.get(Object key) is worth reading
carefully here:

More formally, if this map contains a mapping from a key k to a
value v such that (key==null ? k==null : key.equals(k)), then this
method returns v; otherwise it returns null. (There can be at most
one such mapping.)

Note that key and k don't have to be the same type, and it doesn't
even require k.equals(key) -- equals doesn't need to be reflexive. To
me, this looks like a deliberate design choice.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top