简体   繁体   English

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

[英]What is the difference between using generics and interfaces in methods and classes

I'm wondering what is the difference between using an interface (or a supertype) in a generic class or method and using the generic approach with bounding (T extends Interface). 我想知道在泛型类或方法中使用接口(或超类型)与使用边界的通用方法(T扩展接口)之间有什么区别。

Let's say that I have two classes that implement and interface (naive examples): 假设我有两个实现和接口的类(天真的例子):

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) What would be the difference between writing a method in the following ways: 1)以下列方式编写方法之间的区别是什么:

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

and something like 和类似的东西

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) And if I wanted a class to hold a MeasurableDistance object: 2)如果我想要一个类来保存MeasurableDistance对象:

public class Holder {
    private MeasurableDistance holder;

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

or something like 或类似的东西

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

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

For 1, I think they should be pretty much identical, right? 对于1,我认为它们应该完全相同,对吧? Obviously calling the non generic version 显然称为非通用版本

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);

would always work because computeDistance() would be seeing those objects as MeasurableDistance because of polymorphism. 总是有效,因为computeDistance()会因为多态而将这些对象视为MeasurableDistance。 The generic version instead should work too right? 通用版本应该工作得太对了? The compiler would infer the MeasurableDistance type and add casts accordingly. 编译器将推断MeasurableDistance类型并相应地添加强制转换。 Even if I wanted to call it this way: 即使我想这样称呼它:

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

it would have the same effect, the method would look like 它会产生相同的效果,方法看起来像

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

if I'm not mistaken. 如果我没错的话。 Or, now that I think about it, it should look like this: 或者,现在我考虑一下,它应该是这样的:

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

and it would work because both Person and House are implementing that method. 它会起作用,因为Person和House都在实施这种方法。

So what would be the advantage of the generic method? 那么泛型方法的优点是什么? I've read that it's typesafe because casts are always right, but with the non generic method you won't have casts at all. 我已经读过它的类型安全,因为强制转换总是正确的,但是使用非泛型方法你根本就没有强制转换。

You seem to be confusing the reason to use generics, with the reason to use bounded generics. 你似乎混淆了使用泛型的原因,以及使用有界泛型的原因。

A reason for using bounded generics, implies that there is already a reason to use generics. 使用有界泛型的原因意味着已经有理由使用泛型。

Let me recap. 让我回顾一下。


The reason to use generics is explained in the Java tutorials , and one of the most visible reasons is this: 使用泛型的原因在Java教程中进行了解释,其中一个最明显的原因是:

The following code snippet without generics requires casting: 以下没有泛型的代码片段需要强制转换:

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

When re-written to use generics, the code does not require casting: 重写以使用泛型时,代码不需要转换:

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

The reason to use bounded generics is separate from the reason to use generics. 使用有界泛型的原因与使用泛型的原因是分开的。 If you have a reason to use generics , using bounds let's you require a certain level of functionality of a type: 如果你有理由使用泛型 ,使用边界让你需要一定类型的类型的功能:

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

The above error can be solved by adding a bound: 上面的错误可以通过添加一个绑定来解决:

public class MyClass<T extends Disposable> {...}

The cases that you have shown don't actually have a reason to use bounded generics, like you have pointed out. 你所展示的案例实际上没有理由使用有界泛型,就像你已经指出的那样。 But this is because they don't have a (good) reason to use generics in the first place. 但这是因为他们没有(好的)理由首先使用泛型。

In case 2, generics let you limit the type of object that can be held by Holder : 在第2种情况下,泛型允许您限制Holder可以持有的对象类型:

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

Which isn't very useful. 哪个不是很有用。 There is no real reason to use generics there. 没有真正的理由在那里使用泛型。

But if you were also retrieving the value, generics would be useful: 但是,如果您还要检索该值,则泛型将非常有用:

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

First of all, when you define a method like 首先,当你定义一个类似的方法时

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,
}

There will be no type cast at all. 根本没有类型转换 This method will do exactly the same as with the method signature 此方法与方法签名完全相同

public int computeDistance(MeasurableDistance a, MeasurableDistance b)

which is the reason why the generic type parameters make no sense here; 这就是泛型类型参数在这里没有意义的原因; they don't change anything. 他们不会改变任何事情。


As a rule of thumb, type parameters on methods are useful when you want to declare a relationship between two or more parameters or between parameter(s) and the return type. 根据经验,当您要声明两个或多个参数之间或参数(s)和返回类型之间的关系时,方法上的类型参数很有用。

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

Here, we have a relationship between the parameter and return type, which allows use to use it like: 这里,我们在参数和返回类型之间有一个关系,允许使用它,如:

Person person;
Rectangle localArea;

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

because the generic signature specifies that when we substitute Person for T , we have to pass in a Person instance and are guaranteed to get back a Person instance. 因为通用签名指定当我们用Person替换T ,我们必须传入一个Person实例并保证返回一个Person实例。


Likewise, generic signatures at classes are useful, if we can express a relationship between two or more members of the class. 同样,如果我们可以表达类的两个或更多成员之间的关系,那么类的通用签名是有用的。 Eg a List expresses such relationship as the type of the objects we add or set is the same type we can retrieve via get . 例如, List表示这样的关系,因为我们addset的对象的类型与我们可以通过get检索的类型相同。

So your Holder class would be an appropriate use case for a type parameter, when you add a method to retrieve the encapsulated object, as then, there is a relationship between the storage and retrieval methods. 因此,当您添加一个方法来检索封装对象时,您的Holder类将是类型参数的合适用例,因为存储和检索方法之间存在关系。 As a consequence, there is also a relationship between these two methods and the type of the member variable holding that reference, which is required for correctly implementing the logic. 因此,这两个方法之间也存在关系,并且保存该引用的成员变量的类型是正确实现逻辑所必需的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM