kepe
kepe

Reputation: 252

How to change the generic type of a field in a subclass?

I have a class, A:

import java.util.ArrayList;
import java.util.List;

public class A {

    protected List<Object> objects;

    public A() {
        objects = new ArrayList<Object>();
    }

    public A(List<Object> objects) {
        this.objects = objects;
    }

    public List<Object> getObjects() {
        return objects;
    }

    public void addObject(Object o) {
        getObjects().add(o);
    }

}

I want to subclass it with a class called B, but changing the objects field to a List<Number>:

import java.util.List;

public class B extends A {

    @Override
    public List<Number> getObjects() {
        return (List<Number>) super.getObjects();
    }

    @Override
    public void addObject(Object o) {
        getObjects().add((Number) o);
    }
}

This, however, throws a compiler error. How can I do this correctly?

Upvotes: 0

Views: 841

Answers (2)

Colm Bhandal
Colm Bhandal

Reputation: 3831

This is a key subtlety of how generics work in Java. If a type T extends a type U, we would expect that List<T> extends List<U>. But that's not the case. So when you try to return List<Number> as a subtype of List<Object> the compiler complains.

Note- the complaint isn't about the casting you do inside the function body, which is in itself dangerous. It's about the return type of the function which claims to be an override. In fancy terms, the return type is not "covariant" with the method in the superclass: https://www.tutorialspoint.com/Covariant-return-types-in-Java.

So that explains the "why" behind the compiler error.

As for the "how" to do this correctly. Without seeing the use case, I must say Bret C's Answer is the way I would do it.

Upvotes: 0

BretC
BretC

Reputation: 4199

Introduce a type parameter to your A class...

import java.util.ArrayList;
import java.util.List;

public class A<T> {

    protected List<T> objects;

    public A() {
        objects = new ArrayList<T>();
    }

    public A(List<T> objects) {
        this.objects = objects;
    }

    public List<T> getObjects() {
        return objects;
    }

    public void addObject(T o) {
        getObjects().add(o);
    }

}

Now B doesn't actually need to override the methods...

public class B extends A<Number> {

}

Upvotes: 3

Related Questions