我想知道在通用类或方法中使用接口(或supertype)与使用界限的通用方法之间有什么区别(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),如果我想让一堂课容纳一个测量值对象:
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);
始终是因为Computistance()将这些对象视为多态性的测量值。通用版本应该太正确了吗?编译器将推断出测量值类型并相应地添加铸件。即使我想这样称呼它:
<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();
这会起作用,因为人和房屋都在实施该方法。
那么,通用方法的优势是什么?我读到它是类型的,因为铸件总是正确的,但是使用非通用方法,您根本不会铸造。
您似乎使使用仿制药的理由感到困惑,而使用有限的仿制药。
使用有界通用的原因,这意味着已经有一个使用仿制药的理由。
让我回顾。
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
使用有界通用物的原因与使用仿制药的理由分开。如果您有理由使用generics ,使用界限,让您 reque 类型的一定级别的功能:
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,
}
根本没有类型的cast 。此方法将与方法签名完全相同
public int computeDistance(MeasurableDistance a, MeasurableDistance b)
这就是为什么通用类型参数在这里没有意义的原因;他们什么都不改变。
作为经验法则,当您要声明两个或多个参数之间的关系或参数和返回类型之间的关系时,方法上的类型参数很有用。
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
类将是适合类型参数的适当用例,因为存储和检索方法之间存在关系。结果,这两种方法与符合该参考的成员变量的类型之间也存在关系,这是正确实施逻辑所必需的。