Quirk with generics

  • Thread starter secret decoder ring
  • Start date
A

Arne Vajhøj

secret said:
You're joking. That would have required:
* Creating a new project in my IDE.
* Configuring this.
* Creating a new Java package within the project.
* Creating four new source files within the package.
* All of the copying, pasting, and editing noted above.
* A second round of copying, pasting, and editing (to remove auto-added
top-of-file comments).
* Deleting the project, its package, and the four source files.
That could easily take half an hour or more, and requires messing with
my IDE potentially disturbing the way I'd had it (current project/recent
files, etc.).

How utterly ridiculous.

I think the ridiculous part is that you expect others to do that
to help you but you are not willing to do it yourself.
The ideal is also that every software program have a formal proof of
correctness, but we live in the real world where people have limited
time, energy, and resources.

Formal proof of correctness does not really apply to code that does
not compile.

Arne
 
A

Arne Vajhøj

secret said:
>
Yes. The code was hand-copied (and hand-edited) from actual code.

Which is wrong. It should not be hand-edited. It should be pure
copy paste of the exact code which you have verified to have
the problem.
By saying "no" there, you are pretty much accusing me of lying,

I told you to post the actual code. And you replied that you
did but that did not (by hand editing it).

I consider that inconsistent. Since only one of the statements
can be true. Either it is actual code or you hand-edited it.

For the record then I do not consider that a lie. It is
just a poor excuse demonstrating lack of logical thinking.

Arne
 
M

Mike Schilling

secret said:
You're joking. That would have required:
* Creating a new project in my IDE.
* Configuring this.
* Creating a new Java package within the project.
* Creating four new source files within the package.
* All of the copying, pasting, and editing noted above.
* A second round of copying, pasting, and editing (to remove
auto-added top-of-file comments).
* Deleting the project, its package, and the four source files.
That could easily take half an hour or more, and requires messing
with
my IDE potentially disturbing the way I'd had it (current
project/recent files, etc.).

How utterly ridiculous.

Geez, your IDE really stinks. (I don't know why you couldn't just
create a new class in your existing project that demonstrates the
behavior you want to show, but I'll take your word for it.)
The ideal is also that every software program have a formal proof of
correctness, but we live in the real world where people have limited
time, energy, and resources.

Exactly. If you want me to help you, spend your resources, not mine.
 
L

Lew

Mike said:
Exactly. If you want me to help you, spend your resources, not mine.

Many of the rest of us do engage in the effort necessary to present an SSCCE,
or at least accurate code. That is the standard of the newsgroup, which many
people are able to meet. If one can't stand the heat, one should exit the
kitchen.

Programming is difficult, challenging, exacting work, not for the lazy.
 
L

Lew

secret said:
Yes, it just seemed to me that the compiler can infer such a method must
exist in a Baz implementation, without needing it spelled out.

Lots of people wish that Java were different from what it is, but it isn't.
 
S

secret decoder ring

Arne said:
I think the ridiculous part is that you expect others to do that
to help you

No, I didn't expect anyone at all to do that. In particular, I didn't
need any help at all; I was just noticing a quirk or limitation in the
compiler's ability to make certain logical inferences.
Formal proof of correctness does not really apply to code that does
not compile.

This statement shows that you have completely and utterly missed the point.

My point was that it is unrealistic and unreasonable to expect everyone
to live up to your ideals, in response to your berating me for not
living up to a particular one of yours.

But I suppose I shouldn't be surprised that that sailed right over your
head, given that what I've seen of your postings, spelling-, grammar-,
and attitude-wise, suggests that you're more likely to be a sixth-grader
with ADHD and anger management issues than an adult.
 
S

secret decoder ring

Mike said:
Geez, your IDE really stinks.

Actually, most of the above stems simply from the fact that Java is not
an immediate-mode language like Smalltalk or a certain five-letter
acronym that shall not be named.
(I don't know why you couldn't just create a new class in your existing
project

Pollute the existing project? That suggestion is even sillier.
Exactly. If you want me to help you

I don't. I was making note of a quirk. As my original post made clear, I
already had working code.
 
S

secret decoder ring

Lew said:
Many of the rest of us do engage in the effort necessary to present an
SSCCE, or at least accurate code. That is the standard of the
newsgroup, which many people are able to meet.

This newsgroup shows up in my newsreader as "unmod". That means we are
all equals here. Which in turn means that you, in particular, do not get
to impose standards on the rest of us.

The only place that might legitimately specify such a standard that
"holds weight" would be the newsgroup FAQ/charter. It does not seem to
have a regularly-reposted FAQ and the charter says it's for discussion
of Java programming, and little else.
If one can't stand the heat, one should exit the kitchen.

Programming is difficult, challenging, exacting work, not for the lazy.

These hostile remarks do not seem to fall within the bounds of that
charter, unlike my original question.

In particular, I dislike the sleazily-made implication that I am lazy. I
am not. You, on the other hand, certainly seem to be *something*. I'm
just not sure if that's lazy, anal, or just plain arrogant at this point.
 
S

secret decoder ring

Arne said:
Which is wrong.

No. The code REALLY WAS hand-copied (and hand-edited) from actual code.

I should know. I'm the one who did it.

Repeatedly asserting that my statement is incorrect, when it's clear
that I should know the truth, is tantamount to calling me a liar.

But if anyone is a liar here it is you. I have helpfully restored the
quoted material so that my previous response is in its original, correct
context again, a response to your "no". You had edited the quoted
material in your post to make it seem as if I'd been responding to
something else instead, when I had not. That seems rather dishonest to
me. Hence "if anyone is a liar here it is you".
It should not be hand-edited.

In your opinion.
It should be pure copy paste of the exact code which you have
verified to have the problem.

Ridiculous. The exact code ran to several hundred lines, as I believe
I'd already mentioned previously.
I told you to post the actual code.

One, that is not a logical response to what I wrote. I said you were
pretty much accusing me of lying, and your next line has nothing at all
to do with your implied accusation and completely changes the subject.

Two, I do not take orders from you. Who do you think you are? Lew?
And you replied that you did but that did not

This does not make sense.

Please stop posting here until you have passed sixth-grade English
spelling and grammar. Thank you.
I consider that inconsistent. Since only one of the statements
can be true. Either it is actual code or you hand-edited it.

For the record then I do not consider that a lie. It is
just a poor excuse demonstrating lack of logical thinking.

Think what you want. Only an asshole broadcasts such unpleasant and
irrelevant opinions of other people in public, though. Especially when
they're wrong. :)
 
S

secret decoder ring

Lew said:
Lots of people wish that Java were different from what it is, but it isn't.

But it is not graven in stone, either, and indeed I hear they're open
sourcing it starting with version seven.
 
S

secret decoder ring

Steven said:
I think the compiler has to be able to cope with multiple same-signature
methods permitted at the JVM level, even if forbidden in Java

Do you mean multiple inheritance of non-abstract parents?

Even so, that would apply to the JIT compiler but not to the javac
compiler, which is free to assume that multiple non-abstract supertypes
will not occur, since it is the JAVA compiler (not the Jython, or JRuby,
or anything else that begins with "J", compiler).
I don't follow there - Bar couldn't cover for Foo.

As a return type, it can, since Foo is a supertype of Bar.

If the same abstract method signature is multiply inherited, they are
logically compatible so long as there is a common subtype of all their
return types and a common supertype of each positional set of argument
types, with no difference in their numbers of arguments.

So in particular if Bar extends Foo and we get

Foo method (Foo object)

and

Bar method (Foo object)

it is logically possible to satisfy both requirements in an
implementation with a

<T extends Bar, U super Foo> T method (U object)

or anything that reifies the type parameters within their bounds.

Note that the above regards merely what is logically possible, not what
the current javac does or the current JLS apparently specifies. Nor does
it assert that the latter should necessarily be changed. It merely
indicates that the possibility exists that such a change could occur
without being "impossible" in some sense.

My observation is that the Java compiler does not tend to make very many
inferences, particularly about types, and that the philosophy is for
coders to have to make certain things explicit that in principle could
be inferred because making those things explicit helps to catch errors.
 
J

Joshua Cranmer

secret said:
Well ... except that if something implements both interfaces, surely the
compiler should know that the above methods must be one and the same,
seeing as they have the same signature and will exist in the same class?

Not necessarily. It sees a Baz floating around, and it needs to pick a
method to invoke. The signature of a method includes its return type, so
it has to pick either the Foo method(Foo) or the Bar method(Foo) to
call. These methods are not necessarily the same, and the necessary
bridge magic that normally solves the issue is generated in the Quux
class, not the Baz interface. The interface itself (by virtue of having
all its methods abstract) cannot write its own bridge code.

The wonderful world of erasure. It pains everybody.
 
S

secret decoder ring

Joshua said:
Not necessarily.

Yes necessarily.
It sees a Baz floating around, and it needs to pick a
method to invoke. The signature of a method includes its return type, so
it has to pick either the Foo method(Foo) or the Bar method(Foo) to
call. These methods are not necessarily the same

But they are. If both methods have the same receiver ("this" object) and
the same parameter signature, they must be the same method. A single
Java class cannot have two distinct method(Foo)s, even with different
return types, in it.
The wonderful world of erasure. It pains everybody.

Erasure has nothing to do with the question of whether a single class
can have two distinct method(Foo)s.
 
J

Joshua Cranmer

secret said:
Yes necessarily.

No. Let's take a look at javap here:
jcranmer@quetzalcoatl ~/Desktop $ javap Baz
Compiled from "Quux.java"
interface Baz extends Foo,Bar{
}
jcranmer@quetzalcoatl ~/Desktop $ javap Bar
Compiled from "Quux.java"
interface Bar{
public abstract Bar method(Foo);
}

This is what the compiler sees about the Baz and Bar interfaces. It
actually knows about the T method(S) (a newer version of javap, bundled
with current OpenJDK trunk) will confirm that.

What the compiler will ultimately see is the presence of two methods in
the interface method table:

public abstract Bar method(Foo);
public abstract Foo method(Foo);
But they are. If both methods have the same receiver ("this" object) and
the same parameter signature, they must be the same method. A single
Java class cannot have two distinct method(Foo)s, even with different
return types, in it.

Ah, this is where you are mistaken. Let us discuss the VM for a moment.
When the VM chooses a method to invoke, it's based on the entire
descriptor--including the return type. Covariant returns and generics
erasures mean that you have problems at this point.

Case in point: java.lang.String. If you view its source code (or API),
you will notice that it defines only public int compareTo(String). If
you javap java.lang.String, you will see a public int compareTo(Object).
But it never defined a compareTo(Object), did it?

If you javap java.lang.Comparable, you'll see its sole method:
jcranmer@quetzalcoatl ~/Desktop $ javap java.lang.Comparable
Compiled from "Comparable.java"
public interface java.lang.Comparable{
public abstract int compareTo(java.lang.Object);
}

By the requirements of abstractness, we need to have that
compareTo(Object) class, but we never defined it in source. The compiler
has to generate an implementation:

public int compareTo(java.lang.Object);
Code:
Stack=2, Locals=2, Args_size=2
0: aload_0
1: aload_1
2: checkcast #40; //class java/lang/String
5: invokevirtual #112; //Method compareTo:(Ljava/lang/String;)I
8: ireturn

What javap isn't printing out is the flags--two flags are going to be
set on this method, the ACC_SYNTHETIC flag (it isn't in source), and the
ACC_BRIDGE flag (it's bridging the actual method).

Again, returning to Quux:
jcranmer@quetzalcoatl ~/Desktop $ javap Quux
Compiled from "Quux.java"
public class Quux extends java.lang.Object implements Baz{
public Quux();
public Quux method(Quux);
public static void main(java.lang.String[]);
public Foo method(Foo);
public Bar method(Foo);
}

You see the two methods--the erasure of the interface methods--that were
never defined in the source.

Let's now emulate a hypothetical compiler as it tries to resolve the method.

b1 is of type Baz. Our method lookup returns two candidates with the
correct number of args, the two methods from the interface. Now we hit
§15.12.2.5 of the JLS:
One fixed-arity member method named m is more specific than another
member method of the same name and arity if all of the following
conditions hold:

* The declared types of the parameters of the first member method
are T1, . . . , Tn.
* The declared types of the parameters of the other method are U1,
.. . . , Un.
* If the second method is generic then let R1 ... Rp p >= 1, be its
formal type parameters, let Bl be the declared bound of Rl, 1 <= l <= p,
let A1 ... Ap be the actual type arguments inferred (§15.12.2.7) for
this invocation under the initial constraints Ti << Ui, 1 <= i <= n and
let Si = Ui[R1 = A1, ..., Rp = Ap] 1 <= i <= n; otherwise let Si = Ui 1
<= i <= n.
* For all j from 1 to n, Tj <: Sj.
* If the second method is a generic method as described above then
Al <: Bl[R1 = A1, ..., Rp = Ap], 1 <= l <= p.

T1 = T
U1 = S
S1 = U1 = S

Is T <: S (is T a subtype of S)? We turn to §4.10.2. A type variable is
a subtype of its bound (and therefore only a subtype of its bound and
its bounds' supertypes). So neither method is more specific than the other.

So we have two maximally specific methods:
# If all the maximally specific methods have override-equivalent
(§8.4.2) signatures, then:

* If exactly one of the maximally specific methods is not declared
abstract, it is the most specific method.
* Otherwise, if all the maximally specific methods are declared
abstract, and the signatures of all of the maximally specific methods
have the same erasure (§4.6), then the most specific method is chosen
arbitrarily among the subset of the maximally specific methods that have
the most specific return type. However, the most specific method is
considered to throw a checked exception if and only if that exception or
its erasure is declared in the throws clauses of each of the maximally
specific methods.

# Otherwise, we say that the method invocation is ambiguous, and a
compile-time error occurs.

Are they override-equivalent? Again, another definition.
Two method signatures m1 and m2 are override-equivalent iff either m1 is
a subsignature of m2 or m2 is a subsignature of m1.

The signature of a method m1 is a subsignature of the signature of a
method m2 if either

* m2 has the same signature as m1, or
* the signature of m1 is the same as the erasure of the signature
of m2.

So either the two methods have to be the same signature, or one of the
methods is the erasure of the other. The signatures are
different--remember, we have different parameters T and S--and the
erasure of the first method (method(Foo)) is not the same as the second
(method(S)); the erasure of the second (method(Foo)) is not the same as
the first (method(T)). So the two methods are not override-equivalent.

We therefore hit the ambiguous method invocation clause of the JLS.

You may argue that the JLS is wrong, but the ambiguous method invocation
error conforms to the JLS.
Erasure has nothing to do with the question of whether a single class
can have two distinct method(Foo)s.

The class has two distinct method(Foo)s because of erasure.
 
S

secret decoder ring

Joshua said:

Yes. I have already explained why. I guess I will have to do so again.
Ah, this is where you are mistaken.

No, I am not!

Quoting chapter and verse:

http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.2

It is a compile-time error to declare two methods with
override-equivalent signatures (defined below) in a class.

Two method signatures m1 and m2 are override-equivalent iff either m1 is
a subsignature of m2 or m2 is a subsignature of m1.

The signature of a method m1 is a subsignature of the signature of a
method m2 if either

* m2 has the same signature as m1, or
* (not important in this specific instance)

Two methods have the same signature if they have the same name and
argument types.

So our hypothetical two distinct method(Foo)s have the same name and
argument types -- name "method", one argument, that argument's type Foo.
That means they have the same signature. That, in turn, means that each
signature is a subsignature of the other. EITHER of those is enough to
make the two methods override-equivalent. And it is therefore a
compile-time error to declare both distinct method(Foo)s in a single class.

The JLS says mine is the correct claim.

Note in particular that it says nothing about return types.
You may argue that the JLS is wrong

The interesting thing is that it is YOU who now needs to argue that the
JLS is wrong, or else concede the argument. :)
 
J

Joshua Cranmer

secret said:
So our hypothetical two distinct method(Foo)s have the same name and
argument types -- name "method", one argument, that argument's type Foo.
That means they have the same signature. That, in turn, means that each
signature is a subsignature of the other. EITHER of those is enough to
make the two methods override-equivalent. And it is therefore a
compile-time error to declare both distinct method(Foo)s in a single class.

Let me state my claims in bulleted form. Please indicate which specific
claim you disagree with:

1. We are trying to resolve which method |b1.method(b2)| will call.
2. There are two possible choices for |b1.method(b2)| generated by the
lookup phase:
2a. Foo::method(T) [1]
2b. Bar::method(S) [1]
3. There are no methods in Baz that could be looked up.
4. Foo::method(T) and Bar::method(S) are not subsignatures of each
other, as:
4a. T and S are clearly different.
4b. method(Foo) != method(S).
4c. method(T) != method(Foo).

You have referred to a method(Foo). I see no method(Foo) to be looked
up. Where are you getting the |Foo| from? Please point me to the *exact*
citation where it tells you to replace T and S with Foo.

[1] I should be more precise in that the respective T and S are type
variables from the declaration of class Foo and class Bar, respectively.
Thanks to the OP for making those names distinct such that I don't have
to denote these constantly.
 
A

Arne Vajhøj

secret said:
No. The code REALLY WAS hand-copied (and hand-edited) from actual code.

Sure. Which was wrong. You should have posted the actual code.
Ridiculous. The exact code ran to several hundred lines, as I believe
I'd already mentioned previously.

Try google for SSCCE.
Think what you want. Only an asshole broadcasts such unpleasant and
irrelevant opinions of other people in public, though. Especially when
they're wrong. :)

It is obviously true. Code can not both be the actual code and be
edited.

Arne
 
A

Arne Vajhøj

secret said:
No, I didn't expect anyone at all to do that.

Then you got exactly what you expected: you were told
that your code has syntax errors.

Unless people spend time trying to guess what the crap
you posted actually meant, then that is all they could
write.
In particular, I didn't
need any help at all;

It has been clearly demonstrated in the thread that you do need help
understanding how generics work in Java.
This statement shows that you have completely and utterly missed the point.

My point was that it is unrealistic and unreasonable to expect everyone
to live up to your ideals, in response to your berating me for not
living up to a particular one of yours.

The concept of formal proof of correctness has no relevance for that.

And besides you are wrong. It is the expectation. If you search this
group you will find many many references to the concept SSCCE.

You may make 1 guess about what the last C stands for.

Arne
 

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,774
Messages
2,569,596
Members
45,134
Latest member
Lou6777736
Top