如何安全地#34;使用多态与通用模板?

时间:2014-08-08 18:19:19

标签: java generics polymorphism

我有一个接口,有几个类实现它:

interface Vehicle {}

class Car implements Vehicle {}

class Bicycle implements Vehicle {}

我还有另一个具有getter和setter的接口:

interface Person {
    public ArrayList<Vehicle> getVehicles();
    public void setVehicles (ArrayList<Vehicle> vs);
}

一些类实现它:

class CarOwner implements Person {
    public ArrayList<Car> cars;
    public ArrayList<Car> getVehicles() {
        return cars;
    }
    public void setVehicles(ArrayList<Car> cars) {
        this.cars = cars;
    }
}

class BicycleOwner implements Person {
//...
}

然而,它抱怨setter与接口具有相同的擦除但不覆盖它。我不得不将ArrayList<Vehicle>更改为ArrayList,并将其转换为函数,但它不是&#34;安全&#34; (我知道它应该有效,但其他人可能不会)。如何应对这种情况?


修改

当我将代码更改为答案时,它停止了抱怨。但是当我使用它时,例如

//I have an arraylist of Person
ArrayList<Person> persons;
//...
Vehicle first = persons.get(0).getVehicles().get(0);

它抱怨类型的第一行需要一个参数......但它可能是任何类型的人,因为Arraylist persons可以包含所有类型的人..怎么能这样做?或者它必须离开它没有模板参数?


EDIT2:

我在那里更新了答案,发现当我调用setter时它再次引发错误......

Person<?> personA = persons.get(0);
ArrayList<Vehicle> vs = new ArrayList<Vehicle>();
//vs.add(carA);    
personA.setVehicles(vs);  

最后一行给出错误:This gives error saying The method setVehicles(ArrayList<capture#6-of ?>) in the type Post<capture#6-of ?> is not applicable for the arguments (ArrayList<Vehicle>)。我试图更改为ArrayList<?>,但它不起作用...

4 个答案:

答案 0 :(得分:2)

您的问题是您的界面被声明为接受ArrayList<Vehicle>,但您尝试使用一个ArrayList<Car>的方法来实现它。有人可以打电话给你的界面并合法地将其列入其他类型的扩展车辆列表,这样你的代码就不会编译。

您要做的是让您的Person界面具有通用性,以便它可以采用任何扩展Vehicle的类型,然后使CarOwner成为实现Person<Car>的类:

interface Person<T extends Vehicle> {
    public ArrayList<T> getVehicles();
    public void setVehicles (ArrayList<T> vs);
}

class CarOwner implements Person<Car> {
    public ArrayList<Car> cars;
    public ArrayList<Car> getVehicles() {
        return cars;
    }
    public void setVehicles(ArrayList<Car> cars) {
        this.cars = cars;
    }
}

编辑以回复问题编辑

好吧,如果您要使用原始ArrayList<Person>,那么您实际上只是将类型错误从类定义转移到使用它。参数化Person后,您还需要以这种方式使用它:

    ArrayList<Person<?>> persons = new ArrayList<>();
    // ...
    Vehicle first = persons.get(0).getVehicles().get(0);

您可以在Java here中了解有关通配符的更多信息。

EDIT2

是的,通配符的缺点是编译器不知道类型。在这种情况下,您可以使用Vehicle接口,因为您确实知道所有内容正在扩展的类型:

    ArrayList<Person<Vehicle>> persons = new ArrayList<Person<Vehicle>>();
    // ...
    Person<Vehicle> personA = persons.get(0);
    ArrayList<Vehicle> vs = new ArrayList<Vehicle>();
    //vs.add(carA);    
    personA.setVehicles(vs);

答案 1 :(得分:1)

使您的界面通用。

interface Person<T extends Vehicle> {
   public ArrayList<T> getVehicles();
   public void setVehicles (ArrayList<T> vs);
}

class CarOwner implements Person<Car> {...}

作为对编辑的回应,请使用通用通配符

//I have an arraylist of Person
ArrayList<Person<?>> persons;
//...
Vehicle first = persons.get(0).getVehicles().get(0);

答案 2 :(得分:1)

如果您的Person实例旨在拥有特定类型的车辆,为什么不为该类型引入类型参数?

interface Person<V extends Vehicle> {
    ArrayList<V> getVehicles();
    void setVehicles (ArrayList<V> vs);
}

class CarOwner implements Person<Car> {
    public ArrayList<Car> cars;
    public ArrayList<Car> getVehicles() {
        return cars;
    }
    public void setVehicles(ArrayList<Car> cars) {
        this.cars = cars;
    }
}     

答案 3 :(得分:1)

如果您希望通用类型能够通过实现类来改变,那么您需要使Person通用。类似的东西:

interface Person<V extends Vehicle> {
    ArrayList<V> getVehicles();
}

class CarOwner implements Person<Car>
    @Override
    public ArrayList<Car> getVehicles() {
        return cars;
    }
}

另请注意,在界面中定义ArrayList这样的特定类型是糟糕的设计;几乎没有任何理由限制那种狭隘的回报类型。在这种情况下,如果您确实需要具有随机访问权限,则可以返回RandomAccessList,但List甚至Collection通常是更好的选择。