[英]What is the difference between using generics and interfaces in methods and classes
我想知道在泛型类或方法中使用接口(或超类型)与使用边界的通用方法(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都在实施这种方法。
那么泛型方法的优点是什么? 我已经读过它的类型安全,因为强制转换总是正确的,但是使用非泛型方法你根本就没有强制转换。
你似乎混淆了使用泛型的原因,以及使用有界泛型的原因。
使用有界泛型的原因意味着已经有理由使用泛型。
让我回顾一下。
使用泛型的原因在Java教程中进行了解释,其中一个最明显的原因是:
以下没有泛型的代码片段需要强制转换:
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
}
}
上面的错误可以通过添加一个绑定来解决:
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
首先,当你定义一个类似的方法时
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
表示这样的关系,因为我们add
或set
的对象的类型与我们可以通过get
检索的类型相同。
因此,当您添加一个方法来检索封装对象时,您的Holder
类将是类型参数的合适用例,因为存储和检索方法之间存在关系。 因此,这两个方法之间也存在关系,并且保存该引用的成员变量的类型是正确实现逻辑所必需的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.