Odd behavior with type inference

M

Mike Schilling

While constructing the FilteredCollection class, I ran into the following
oddity with type inference. The following class fails to compile (using
javac 1.6.0_20):

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;


public class Oddity<C> implements Iterable<C>
{
public Iterator<C> iterator()
{
return null;
}

public static void main(String[] args)
{
List list = Arrays.asList(1, "a", false);
for (String s : getOddity(list, String.class)) // ******
{
}
}

public static<T,C> Iterable<C> getOddity(Collection<T> coll, Class<C>
clazz)
{
return null;
}
}

The error, at the starred line, is

incompatible types
found : java.lang.Object
required: java.lang.String

That is, it looks as if the inference that getOddity should return an
Iterable<String> fails.

However, it works perfectly if the declaration of "list" in the previous
line is changed to

List<?> list

What's the deal here? Does the presence of a raw type interfere with type
inference?
 
M

markspace

public static<T,C> Iterable<C> getOddity(Collection<T> coll, Class<C>
clazz)
That is, it looks as if the inference that getOddity should return an
Iterable<String> fails.


I read that reversed: that it was "coll" that was assumed to be of type
"T" = String. Sure enough, when I remove the T from the static method,
and replace the type parameter of coll to be ?, then it also compiles.

Seems like a bug either way, unless there some special rule where T is
only used in one place in a parametrized method definition. That would
be kinda odd though.
 
T

Tom Anderson

for (String s : getOddity(list, String.class)) // ******

If you write this:

Iterable<String> oddity = getOddity(list, String.class);
for (String s : oddity)

it works. I can't say why, but perhaps the rules are written in terms of
inference from the types of variables, rather than expressions?

tom
 
M

Mike Schilling

Tom Anderson said:
If you write this:

Iterable<String> oddity = getOddity(list, String.class);
for (String s : oddity)

it works. I can't say why, but perhaps the rules are written in terms of
inference from the types of variables, rather than expressions?

That compiles for me, but with the warning

Note: C:\java\Stuff\src\Oddity.java uses unchecked or unsafe operations.

IntelliJ gives a bit more detail:

Unchecked assignment from Iterable to Iterable<String>

So the type inference still fails, but now in a way that javac considers
forgivable.
 
L

Lew

Mike said:
That compiles for me, but with the warning

Note: C:\java\Stuff\src\Oddity.java uses unchecked or unsafe operations.

IntelliJ gives a bit more detail:

Unchecked assignment from Iterable to Iterable<String>

So the type inference still fails, but now in a way that javac considers
forgivable.

You can't infer the generic type from the raw type. So it's not type
inference here but type conversion.

The original method:
public static<T,C> Iterable<C> getOddity(Collection<T> coll, Class<C> clazz)
{
return null;
}

was passed a raw-typed 'coll' argument. This gives no information about what
'T' is.

When the collection type became non-raw with the '<?>' generic parm, then the
type inference engine had enough to work with.

Why it couldn't ignore the 'T' and infer the 'C' is a mystery, but clearly
connected with having broken the whole generics thing with a raw type in the
first place. Apparently once the foundation is cracked the entire edifice is
unstable.

More evidence for "don't do that". There's no reason for raw types any more
anyway.
 
M

Mike Schilling

Lew said:
You can't infer the generic type from the raw type. So it's not type
inference here but type conversion.

The original method:

was passed a raw-typed 'coll' argument. This gives no information about
what 'T' is.

When the collection type became non-raw with the '<?>' generic parm, then
the type inference engine had enough to work with.

Why it couldn't ignore the 'T' and infer the 'C' is a mystery, but clearly
connected with having broken the whole generics thing with a raw type in
the first place. Apparently once the foundation is cracked the entire
edifice is unstable.


That does appear to be the case. I don’t think the JLS helps here, but it's
really not great on the the fine details of generics.
 
K

Kevin McMurtrie

markspace said:
I read that reversed: that it was "coll" that was assumed to be of type
"T" = String. Sure enough, when I remove the T from the static method,
and replace the type parameter of coll to be ?, then it also compiles.

Seems like a bug either way, unless there some special rule where T is
only used in one place in a parametrized method definition. That would
be kinda odd though.

Generics are unfortunately all-or-nothing. You're lucky that it didn't
compile. Some flaws in generics results in code that compiles but
logically can't execute (ClassCastException is thrown).
 
M

Mike Schilling

Kevin McMurtrie said:
Generics are unfortunately all-or-nothing. You're lucky that it didn't
compile. Some flaws in generics results in code that compiles but
logically can't execute (ClassCastException is thrown).

This case is odder, though. The fact that parameter 1 was a raw type
interfered with an completely valid inference to be drawn from parameter 2.
 
J

Joshua Cranmer

it works. I can't say why, but perhaps the rules are written in terms of
inference from the types of variables, rather than expressions?

It infers expressions, but there is one case where it doesn't:
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.

Most painfully, this inference does not happen if you are immediately
returning the variable, which would have been helpful in some of my code.

I would also like to point out that mixing generics and raw types is a
recipe for disaster. And then you get rare types, forever embedded in
Java because List.class is Class<List> and not Class<List<?>>.
 

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,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top