我想知道在泛型类或方法中使用接口(或超类型)与使用带有边界的泛型方法(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都在实现这种方法。
那么泛型方法的优势是什么?我已经读过它的类型安全因为演员阵容总是正确的,但是使用非通用的方法你根本就没有演员阵容。
答案 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
表达了与add
或set
对象类型相关的关系,我们可以通过get
检索该类型。
因此,当您添加一个方法来检索封装对象时,您的Holder
类将是类型参数的合适用例,因此,存储和检索方法之间存在关系。因此,这两个方法之间也存在关系以及保存该引用的成员变量的类型,这是正确实现逻辑所必需的。