Strange compile error involving generics (short code snippet included)

O

Oliver Wong

I've got two pieces of code; one of them compiles without error, and the
other one doesn't. I can't figure out why. Here's the code snippets:

<compilesFine>
import java.util.Vector;

public class HistoryObjectsManager {
public void doSomethingWithRecord(Record record) {
for (Integer integer : record.getVector()) {
//Does nothing.
}
}
}

abstract class Record {
public Vector<Integer> getVector() {
return new Vector<Integer>();
}
}
</compilesFine>

<generatesCompileError>
import java.util.Vector;

public class HistoryObjectsManager {
public void doSomethingWithRecord(Record record) {
for (Integer integer : record.getVector()) {
//Does nothing.
}
}
}

abstract class Record<A> {
public Vector<Integer> getVector() {
return new Vector<Integer>();
}
}
</generatesCompileError>

The only difference between the two is that Record has a type parameter.
The compile error I get is:

<errorMessage>
HistoryObjectsManager.java:5: incompatible types
found : java.lang.Object
required: java.lang.Integer
for (Integer integer : record.getVector()) {
^
</errorMessage>

I'm using JavaC 1.5.0.

Is this a bug in the compiler or a misunderstanding on my part about
generics?

- Oliver
 
J

John C. Bollinger

Oliver said:
I've got two pieces of code; one of them compiles without error, and the
other one doesn't. I can't figure out why. Here's the code snippets:

<compilesFine>
import java.util.Vector;

public class HistoryObjectsManager {
public void doSomethingWithRecord(Record record) {
for (Integer integer : record.getVector()) {
//Does nothing.
}
}
}

abstract class Record {
public Vector<Integer> getVector() {
return new Vector<Integer>();
}
}
</compilesFine>

<generatesCompileError>
import java.util.Vector;

public class HistoryObjectsManager {
public void doSomethingWithRecord(Record record) {
for (Integer integer : record.getVector()) {
//Does nothing.
}
}
}

abstract class Record<A> {
public Vector<Integer> getVector() {
return new Vector<Integer>();
}
}
</generatesCompileError>

The only difference between the two is that Record has a type parameter.
The compile error I get is:

<errorMessage>
HistoryObjectsManager.java:5: incompatible types
found : java.lang.Object
required: java.lang.Integer
for (Integer integer : record.getVector()) {
^
</errorMessage>

I'm using JavaC 1.5.0.

Is this a bug in the compiler or a misunderstanding on my part about
generics?

I don't know, but I have seen similar. It seems that the compiler
ignores all type parameters associated with a "raw" parameterized type,
such as you have in the second case. This may be intended to provide
compilation diagnostics equivalent to those of a pre-1.5 compiler when
compiling mixed 1.[234]/1.5 codes. In any case, if you don't care what
the Record's type parameter is then the most appropriate Java 1.5 way to
write the method is:

public void doSomethingWithRecord(Record<?> record) {
for (Integer integer : record.getVector()) {
//Does nothing.
}
}

I haven't actually tested it, but I bet it will do what you want. It
has the further advantage of documenting that it in fact works with a
Record type having any type argument, which your original code does not do.
 
I

Ingo R. Homann

Hi,

Oliver said:
<generatesCompileError>
import java.util.Vector;

public class HistoryObjectsManager {
public void doSomethingWithRecord(Record record) {
for (Integer integer : record.getVector()) {
//Does nothing.
}
}
}

abstract class Record<A> {
public Vector<Integer> getVector() {
return new Vector<Integer>();
}
}
</generatesCompileError>

This should work:

public void doSomethingWithRecord(Record<Integer> record) {

Ciao,
Ingo
 
R

Ross Bamford

import java.util.Vector;
public class HistoryObjectsManager {
public void doSomethingWithRecord(Record record) {
for (Integer integer : record.getVector()) {
//Does nothing.
}
}
}
abstract class Record<A> {
public Vector<Integer> getVector() {
return new Vector<Integer>();
}
}

Heres how I see it. In the version above, Record is a generic class. When
you say:

public void doSomethingWithRecord(Record record) {
for (Integer integer : record.getVector()) {
//Does nothing.
}
}

You're not just saying 'any type of record', but 'an untyped record'.
Either the compiler assumes that you're not interested in generics at all,
or it has no way to distinguish between literal types (like <Integer>) and
types that are inferred (e.g. if instead of integer it was <? super
Integer>). So, everything behaves as if there are no parameters, and the
vector returns a untyped iterator (of Object).

If you change the method:

public void doSomethingWithRecord(Record<?> record) {
for (Integer integer : record.getVector()) {
//Does nothing.
}
}

You'll find it works, because you're now saying 'any type of Record'.
 
O

Oliver Wong

Ross Bamford said:
Heres how I see it. In the version above, Record is a generic class. When
you say:

public void doSomethingWithRecord(Record record) {
for (Integer integer : record.getVector()) {
//Does nothing.
}
}

You're not just saying 'any type of record', but 'an untyped record'.
Either the compiler assumes that you're not interested in generics at all,
or it has no way to distinguish between literal types (like <Integer>) and
types that are inferred (e.g. if instead of integer it was <? super
Integer>). So, everything behaves as if there are no parameters, and the
vector returns a untyped iterator (of Object).

If you change the method:

public void doSomethingWithRecord(Record<?> record) {
for (Integer integer : record.getVector()) {
//Does nothing.
}
}

You'll find it works, because you're now saying 'any type of Record'.

Notice though that the getVector() method returns a Vector<Integer>,
regardless of the type of A. This was a simplified code snippet; in the
original code, A did affect some methods, but it wasn't affecting the
getVector() method. I.e. the original code looked something like this:

<originalCode>
public class Record<A> {
private A myValue;

public Vector<Integer> getVector() {
return null;
}

public A getInternalValue() {
return myValue;
}
}
</originalCode>

So no matter WHAT kind of record (whether it be Record<Object>,
Record<String> or anything else), Record's .getVector() method should ALWAYS
return a Vector<Integer>.

- Oliver
 
O

Oliver Wong

John C. Bollinger said:
In any case, if you don't care what the Record's type parameter is then
the most appropriate Java 1.5 way to write the method is:

public void doSomethingWithRecord(Record<?> record) {
for (Integer integer : record.getVector()) {
//Does nothing.
}
}

I haven't actually tested it, but I bet it will do what you want. It has
the further advantage of documenting that it in fact works with a Record
type having any type argument, which your original code does not do.

Yeah, this does fix the compile error and does seem to reflect the
"intent" of the code. Thanks.

- Oliver
 

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,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top