Edward J Beckett
Edward J Beckett

Reputation: 5140

How To Ensure the Validity of Parameterized Types Prior to Object Instantiation

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

Answers (2)

Braj
Braj

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

Read more...


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

pauljwilliams
pauljwilliams

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

Related Questions