在方法和类中使用泛型和接口有什么区别

时间:2016-06-19 13:01:52

标签: java generics interface type-erasure

我想知道在泛型类或方法中使用接口(或超类型)与使用带有边界的泛型方法(T扩展接口)之间有什么区别。

让我们说我有两个实现和接口的类(天真的例子):

public interface MeasurableDistance {
    public Point getPosition();
}

public class Person implements MeasurableDistance {
    private Point position;

    public Point getPosition() {
        return position;
    }
}

public class House implements MeasurableDistance {
    private Point position;

    public Point getPosition() {
        return position;
    }
}

1)以下列方式编写方法之间的区别是什么:

public int computeDistance(MeasurableDistance a, MeasurableDistance b) {
    Point a = a.getPosition();
    Point b = b.getPosition();
    //compute distance
    return distance,
}

等等

public <T extends MeasurableDistance, S extends MeasurableDistance> int computeDistance(T a, S b) {
    Point a = a.getPosition();
    Point b = b.getPosition();
    //compute distance
    return distance,
}

2)如果我想要一个类来保存MeasurableDistance对象:

public class Holder {
    private MeasurableDistance holder;

    public Holder() {};
    public add(MeasurableDistance a) {
        holder = a;
    }
}

或类似

public class Holder<T extends MeasurableDistance> {
    private T holder;

    public Holder<T>() {};
    public add(T a) {
        holder = a;
    };
}

对于1,我认为它们应该完全相同,对吧?显然调用非通用版本

Person a = new Person();
Person b = new Person();
House c = new House();
House d = new House();
computeDistance(a,c);
computeDistance(a,b);
computeDistance(c,d);

总是有效,因为computeDistance()会因为多态而将这些对象视为MeasurableDistance。通用版本应该工作得太对了?编译器将推断MeasurableDistance类型并相应地添加强制转换。即使我想这样称呼它:

<Person, House>computeDistance(a,c);

它会产生相同的效果,方法看起来像

    Point a = (MeasurableDistance) a.getPosition();
    Point b = (MeasurableDistance) b.getPosition();

如果我没有弄错的话。或者,现在我考虑一下,它应该是这样的:

    Point a = (Person) a.getPosition();
    Point b = (House) b.getPosition();

它会起作用,因为Person和House都在实现这种方法。

那么泛型方法的优势是什么?我已经读过它的类型安全因为演员阵容总是正确的,但是使用非通用的方法你根本就没有演员阵容。

2 个答案:

答案 0 :(得分:3)

你似乎混淆了使用泛型的原因,以及使用有界泛型的原因。

使用有界泛型的原因意味着已经有理由使用泛型。

让我回顾一下。

Java tutorials解释了使用泛型的原因,其中一个最明显的原因是:

  

以下没有泛型的代码段需要强制转换:

List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);
     

当重写使用泛型时,代码不需要转换:

List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0);   // no cast

使用有界泛型的原因与使用泛型的原因是分开的。 如果您有理由使用泛型,则使用边界,您需要某种类型的功能:

public class MyClass<T> {
    private List<T> list;
    ...
    public void disposeAll() {
        for(T e : list)
            e.dispose(); // compile time error
    }
}

可以通过添加bound:

来解决上述错误
public class MyClass<T extends Disposable> {...}

您所展示的案例实际上并没有理由使用有界的泛型,就像您指出的那样。但这是因为他们没有(好的)理由首先使用仿制药。

在案例2中,泛型允许您限制Holder可以保留的对象类型:

Holder<House> holder = new Holder<>();
holder.add(new Person()); // compile time error
holder.add(new House());

哪个非常有用。没有真正的理由在那里使用泛型。

但是如果你也在检索这个值,那么泛型会很有用:

Holder<House> holder = new Holder<>();
// holder.add(new Person());
holder.add(new House());
House h = holder.get(); // no cast

答案 1 :(得分:3)

首先,当您定义类似

的方法时
public <T extends MeasurableDistance, S extends MeasurableDistance>
    int computeDistance(T a, S b) {

    Point a = a.getPosition();
    Point b = b.getPosition();
    //compute distance
    return distance,
}

根本没有没有类型转换。此方法与方法签名

完全相同
public int computeDistance(MeasurableDistance a, MeasurableDistance b)

这就是泛型类型参数在这里没有意义的原因;他们不会改变任何事情。

根据经验,当您要声明两个或多个参数之间或参数(s)和返回类型之间的关系时,方法上的类型参数很有用。

public <T extends MeasurableDistance> T validate(T a, Rectangle area) {
    Point p = a.getPosition();
    if(!area.contains(p))
        throw new IllegalArgumentException();
    return a,
}

这里,我们在参数和返回类型之间有一个关系,允许使用它像:

Person person;
Rectangle localArea;

public void setPerson(Person newPerson) {
    this.person = validate(newPerson, localArea);
}

因为通用签名指定当我们将Person替换为T时,我们必须传入Person实例并保证返回Person实例。

同样,如果我们可以在类的两个或更多成员之间表达关系,那么类的通用签名是有用的。例如。 List表达了与addset对象类型相关的关系,我们可以通过get检索该类型。

因此,当您添加一个方法来检索封装对象时,您的Holder类将是类型参数的合适用例,因此,存储和检索方法之间存在关系。因此,这两个方法之间也存在关系以及保存该引用的成员变量的类型,这是正确实现逻辑所必需的。