Reputation: 5140
While studying Item 23 of Effective Java 2nd Edition I decided to attempt instantiating an object with a constructor that has a raw collection with an argument list of mixed objects. I assumed the compiler would pick up that I was attempting to pass a raw type to the constructor. However, the compiler only provides an unchecked exception warning, "at the caller, not the callee."
The class compiles fine, further supporting the statements by Josh Bloch not to use raw types in new code. The fact this compiles is a bit disturbing to me.
Q? How can one ensure the type safety of a class prior to instantiating the object short of having to manually check for object validity in the constructor? The following only provided an unchecked assignment warning from "the caller... in main" How could one defensively program against this.
public class RawType {
private final Collection<Example> example;
public RawType( Collection<Example> example ) {
this.example = example;
}
public Collection<Example> getExample() {
return example;
}
public static void main( String[] args ) {
Collection unsafe = new ArrayList();
unsafe.add( new Example() );
unsafe.add( new Corruption() );
unsafe.add( new Example() );
RawType rawType = new RawType( unsafe ); // Corruption
for(Object type : rawType.getExample()) {
System.out.println(type.getClass().getName()); // Mixed Classes...
}
}
static class Corruption {}
static class Example{}
}
Upvotes: 1
Views: 63
Reputation: 46861
Below lines compile but generate warnings as well.
Collection unsafe = new ArrayList(); // RAW type
unsafe.add(1); // no compile time error (just warning)
Collection<String> example = unsafe; // assign RAW type to parametrized type
example.add(1); // compile time error
Better use Generic collection to avoid such situation then add the values in it. Never mix RAW and parametrized type.
Have a look at below code:
Collection<Integer> unsafe = new ArrayList<Integer>();
unsafe.add(1);
Collection<String> example = unsafe; // compile time error
If you can't avoid RAW type then use below code in the constructor:
Collection unsafe = new ArrayList();
unsafe.add(1);
unsafe.add("hello");
// in the constructor, just add String values from the unsafe collection
Collection<String> example = new ArrayList<String>();
for(Object value:unsafe){
if(value instanceof String){
example.add((String)value);
}
}
Upvotes: 1
Reputation: 19225
In order to make java generics compatible with code written before they were introduced, you can use an untyped collection anywhere that a typed collection is expected. So a Collection (with no type parameter) can be passed to a constructor that expects a Collection.
So in answer to your collection, you can only be safe by being careful when you add objects to an untyped colelction. If you want runtime sadfety you can manually validate the type of objects as you retreive them from the collection.
Upvotes: 0