Generics double redirection compile error: why?

H

Hendrik Maryns

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi

I have some code like this which I am trying to generify:

private static <T> Comparator<T> reverse(final Comparator<T> wrapped) {
return new Reverse<T>(wrapped);
}

public static <T> Comparator<T> switchFirst() {
return new SwitchFirst<T>();
}

public static <T> Comparator<T> switchLast() {
return reverse(switchFirst());
}

Where Reverse<T> and SwitchFirst<T> extends Comparator<T>. Reverse<T>
does the obvious, whereas SwitchFirst<T> is some idiosyncratic stuff.

However:

Type mismatch: cannot convert from Comparator<Object> to Comparator<T>

(This is in Eclipse).

Why?

SSCCE:

import java.util.Comparator;
public class Test {
private static <T> Comparator<T> reverse(final Comparator<T> wrapped) {
return new Reverse<T>(wrapped);
}
private static class Reverse<T> implements Comparator<T> {
private final Comparator<T> wrapped;
/**
* Creates a Comparator with reverse logic
*/
public Reverse(final Comparator<T> wrapped) {
this.wrapped = wrapped;
}
public int compare(final T left, final T right) {
return -this.wrapped.compare(left, right);
}
}
public static <T> Comparator<T> switchFirst() {
return new SwitchFirst<T>();
}
public static <T> Comparator<T> switchLast() {
return Test.reverse(Test.switchFirst());
}
private static class SwitchFirst<T> implements Comparator<T> {
public int compare(final T left, final T right) {
final boolean l = left instanceof Integer;
final boolean r = right instanceof Integer;
// I replaced Switch with Integer here, just for example’s sake
if (l ^ r) {
if (l) {
return -1;
}
return 1;
}
return 0;
}
}

}


H.
- --
Hendrik Maryns
http://tcl.sfs.uni-tuebingen.de/~hendrik/
==================
Ask smart questions, get good answers:
http://www.catb.org/~esr/faqs/smart-questions.html
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.9 (GNU/Linux)
Comment: Using GnuPG with SUSE - http://enigmail.mozdev.org

iEYEARECAAYFAkjzd98ACgkQBGFP0CTku6PcagCgv83OqBV19LUgeayZOS1rBlLs
CAsAn22gWhX+4tW1aCuK/1wU+S0r/KYc
=UePx
-----END PGP SIGNATURE-----
 
J

Joshua Cranmer

Hendrik said:
Type mismatch: cannot convert from Comparator<Object> to Comparator<T>

(This is in Eclipse).

Why?

First things first: replacing the double method call with storage to a
variable makes everything work.

The short answer: the call to switchLast needs to infer type arguments.
Which type arguments?

§15.12.2.8 holds the key (skipping pages of dense prose that tell you
how to infer type arguments based on arguments, which do not exist here):

If any of the method's type arguments were not inferred from the types
of the actual arguments, they are now inferred as follows.
* If the method result occurs in a context where it will be subject
to assignment conversion (§5.2) to a type S, then let R be the declared
result type of the method, and let R' = R[T1 = B(T1) ... Tn = B(Tn)]
where B(Ti) is the type inferred for Ti in the previous section, or Ti
if no type was inferred.

In layman's terms: If we're assigning the return value to something,
make the inferred type arguments work out to that assignment value. The
workaround I gave does that.

Next: Any remaining type variables that have not yet been inferred are
then inferred to have type Object

i.e., "We can't decide what to infer the type variables. They're now
Object."

Another way of doing this, without assigning to a temporary, is to tell
the compiler the type variables:

return reverse(Test.<T>switchFirst());

Why can't it work backwards? The compiler needs the type of the
arguments to resolve the method it needs to call, so it has to resolve
the return type of switchFirst before reasoning about how to do the
reverse call. Type parameters appearing only in return values mean you
need to somehow know what the return value "should" be before inferring
them; passing it immediately in as an argument means that the compiler
has no context as to what the return value should be, so it gives up.

I suspect that weird cases like these are why the Java developers
allowed you to manually specify type arguments in method calls.

Hope this makes sense; type parameter inference is one of the more
difficult portions of the JLS to read.
 
D

Daniel Pitts

Hendrik said:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi

I have some code like this which I am trying to generify:

private static <T> Comparator<T> reverse(final Comparator<T> wrapped) {
return new Reverse<T>(wrapped);
}

public static <T> Comparator<T> switchFirst() {
return new SwitchFirst<T>();
}

public static <T> Comparator<T> switchLast() {
return reverse(switchFirst());
}

Where Reverse<T> and SwitchFirst<T> extends Comparator<T>. Reverse<T>
does the obvious, whereas SwitchFirst<T> is some idiosyncratic stuff.

However:

Type mismatch: cannot convert from Comparator<Object> to Comparator<T>

(This is in Eclipse).

Why? [...snip...]
public static <T> Comparator<T> switchLast() {
return Test.reverse(Test.switchFirst());
}
Try this instead:
public static <T> Comparator<T> switchLast() {
return Test.<T>reverse(Test.<T>switchFirst());
}

The problem is that you're being to "tricky" for the compiler, so you
have to "spell it out".
 
H

Hendrik Maryns

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Joshua Cranmer schreef:
Hendrik said:
Type mismatch: cannot convert from Comparator<Object> to Comparator<T>

(This is in Eclipse).

Why?

First things first: replacing the double method call with storage to a
variable makes everything work.

The short answer: the call to switchLast needs to infer type arguments.
Which type arguments?

§15.12.2.8 holds the key (skipping pages of dense prose that tell you
how to infer type arguments based on arguments, which do not exist here):

If any of the method's type arguments were not inferred from the types
of the actual arguments, they are now inferred as follows.
* If the method result occurs in a context where it will be subject
to assignment conversion (§5.2) to a type S, then let R be the declared
result type of the method, and let R' = R[T1 = B(T1) ... Tn = B(Tn)]
where B(Ti) is the type inferred for Ti in the previous section, or Ti
if no type was inferred.

In layman's terms: If we're assigning the return value to something,
make the inferred type arguments work out to that assignment value. The
workaround I gave does that.

Next: Any remaining type variables that have not yet been inferred are
then inferred to have type Object

i.e., "We can't decide what to infer the type variables. They're now
Object."

Another way of doing this, without assigning to a temporary, is to tell
the compiler the type variables:

return reverse(Test.<T>switchFirst());

Why can't it work backwards? The compiler needs the type of the
arguments to resolve the method it needs to call, so it has to resolve
the return type of switchFirst before reasoning about how to do the
reverse call. Type parameters appearing only in return values mean you
need to somehow know what the return value "should" be before inferring
them; passing it immediately in as an argument means that the compiler
has no context as to what the return value should be, so it gives up.

I suspect that weird cases like these are why the Java developers
allowed you to manually specify type arguments in method calls.

Hope this makes sense; type parameter inference is one of the more
difficult portions of the JLS to read.

Thanks for a very clear explanation!
H.
- --
Hendrik Maryns
http://tcl.sfs.uni-tuebingen.de/~hendrik/
==================
Ask smart questions, get good answers:
http://www.catb.org/~esr/faqs/smart-questions.html
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.9 (GNU/Linux)
Comment: Using GnuPG with SUSE - http://enigmail.mozdev.org

iEYEARECAAYFAkj0V3kACgkQBGFP0CTku6Ph0gCg0wfa7tHrvlrZcrshaUtAACTu
zu8AoLFIjEGQLkiPadz2+4tH8sk/eFNo
=kvQo
-----END PGP SIGNATURE-----
 

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,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top