简体   繁体   English

在抽象java class中正确使用generics?

[英]Proper use of generics in abstract java class?

EDIT : This question is not well worded, and the provided answer is correct in a literal sense but did not teach me how to attain what I needed.编辑:这个问题措辞不好,提供的答案在字面上是正确的,但没有教我如何获得我需要的东西。 If you are struggling with the same problem, this is what finally helped me: How to enforce child class behavior/methods when the return types of these methods depends on the child class?如果您遇到同样的问题,这最终帮助了我: 当这些方法的返回类型取决于子 class 时,如何强制执行子 class 行为/方法?

I am trying to implement a basic matrix class from a boilerplate abstract class I wrote.我正在尝试从我写的样板抽象 class 中实现一个基本矩阵 class 。 There will be several implementations of this abstract class, each one using a different math library, which I will then test for speed.这个抽象 class 将有几个实现,每个都使用不同的数学库,然后我将测试速度。

Each implementation will hold its data in that library's native matrix data structure.每个实现都将其数据保存在该库的本机矩阵数据结构中。 I think this is a use case for generics.认为这是 generics 的用例。 At this point I think I've read too many tutorials and watched too many videos, as I just can't seem to figure out all the right places to put the T Notation to make this work correctly.在这一点上,我认为我已经阅读了太多教程并观看了太多视频,因为我似乎无法找出所有正确的位置来放置T表示法以使其正常工作。

So my question is twofold:所以我的问题是双重的:

  1. Have I misused or missed the point of generics?我是否误用或错过了 generics 的要点?
  2. If not, what is the correct syntax for their use?如果不是,它们使用的正确语法是什么?

I've read the docs plus about three different tutorials and still can't understand.我已经阅读了文档以及关于三个不同教程的内容,但仍然无法理解。

Here is what I've tried:这是我尝试过的:

public abstract class BaseMatrix<T> {

    protected int[] shape;
    protected int nrows;
    protected int ncols;
    protected T data; // <--- Here is the generic data --->

    public BaseMatrix(int rows, int cols){
        this.nrows = rows;
        this.ncols = cols;
        this.shape = new int[]{nrows, ncols};
    }    

    public abstract BaseMatrix mmul(BaseMatrix other);

And here is my implementation:这是我的实现:

public class ND4JDenseMatrix extends BaseMatrix{
    

    // private INDArray data;

    public ND4JDenseMatrix(int rows, int cols) {
        super(rows, cols);
        this.data = Nd4j.zeros(this.shape); <--- Here is the non-generic data --->
    }


    @Override
    public ND4JDenseMatrix mmul(ND4JDenseMatrix other) {
      ND4JDenseMatrix result = new ND4JDenseMatrix(nrows, ncols);
      result.data = data.mmul(other.data);
      return result;
    }

The error is: Method does not override method from its superclass.错误是: Method does not override method from its superclass.

hold its data in that library's native matrix data structure.将其数据保存在该库的本机矩阵数据结构中。 I think this is a use case for generics.我认为这是 generics 的用例。

Generics serves to link things. Generics 用于链接事物。 You declared the type variable with <T> , and you've used it in, as far as your paste goes, exactly one place (a field, of type T).您使用<T>声明了类型变量,并且就您的粘贴而言,您已经在一个地方(T 类型的字段)中使用了它。 That's a red flag;这是一面红旗; generally, given that it links things, if you use it in only one place that's usually a bad sign.一般来说,鉴于它链接事物,如果你只在一个地方使用它,这通常是一个不好的迹象。

Here's what I mean: Imagine you want to write a method that says: This method takes 2 parameters and returns something.这就是我的意思:想象你想写一个方法说:这个方法接受 2 个参数并返回一些东西。 This code doesn't particularly care what you toss in here, but, the parameters must be the same type and I return something of that type too.这段代码并不特别关心你在这里折腾什么,但是,参数必须是相同的类型,我也返回该类型的东西。 You want to link the type of the parameter, the type of the other parameter, and the return type together.您希望参数的类型、另一个参数的类型和返回类型链接在一起。

That is what generics is for.就是 generics 的用途。

It may apply here, if we twist our minds a bit: You want to link the type of the data field to a notion that some specific implementation of BaseMatrix can only operate on some specific type, eg ND4JMatrix.如果我们稍微扭转一下想法,它可能适用于此:您希望将data字段的类型与 BaseMatrix 的某些特定实现只能对某些特定类型(例如 ND4JMatrix)进行操作的概念联系起来。

However, mostly, no, this doesn't strike me as proper use of generics.但是,大多数情况下,不,这并没有让我觉得正确使用 generics。 You can avoid it altogether quite easily: Just.. stop having that private T data;你可以很容易地完全避免它:只是......停止拥有private T data; field.场地。 What good is it doing you here?你在这里有什么好处? You have no idea what type that is, you don't even know if it is serializable.不知道那是什么类型,你甚至不知道它是否是可序列化的。 You know nothing about it, and the compiler confirms this: There is absolutely not one iota you can do with that object, except things you can do to all objects which are generally quite uninteresting.你对此一无所知,编译器证实了这一点:你绝对不能用 object 做任何事情,除了你可以对所有通常很无趣的对象做的事情。 You can call .toString() on it, synchronize on it, maybe invoke .hashCode() , that's about it.您可以在其上调用.toString() ,对其进行同步,或者调用.hashCode() ,仅此而已。

Why not just ditch that field?为什么不直接放弃那个领域? The implementation can make the field, no need for it to be in base!实现可以使字段,不需要它在基地!

public class ND4JDense extends BaseMatrix {
    private ND4JMatrix data; // why not like this?
}

(This code assumes 'ND4JMatrix' is the proper data type you desire here, a thing that can is the internal representation for the data in the ND4J impl). (此代码假定 'ND4JMatrix' 是您在此处所需的正确数据类型,可以是 ND4J impl 中数据的内部表示)。

However, if you must, yeah, you can use generics here.但是,如果必须,是的,您可以在此处使用 generics。 You've type-varred BaseMatrix, and that means all usages of BaseMatrix must be parameterized .您已经键入了变量 BaseMatrix,这意味着BaseMatrix 的所有用法都必须参数化 That's the part you messed up in your code.那是您在代码中搞砸的部分。 If we go with your plan of a type-parameterized BaseMatrix class and a field of type T, the right code is:如果我们 go 与您的类型参数化 BaseMatrix class 和类型 T 字段的计划,正确的代码是:

public class ND4JDense extends BaseMatrix<ND4JMatrix> {
   ...
}

I wouldn't, however, do it this way (I'd go with having the impl have the field, much simpler, no need to bother anybody with the generics).但是,我不会这样做(我希望 go 拥有该领域,简单得多,无需用泛型打扰任何人)。 Unless, of course, you DO have an actual need for that field and it IS part of BaseMatrix's API.当然,除非您确实需要该字段并且它是BaseMatrix的 API 的一部分。 For example, if you want this:例如,如果你想要这个:

public class BaseMatrix<T> {
    public T getData() { return data; }
}

then it starts to make more sense.然后它开始变得更有意义。 With that, you can write the following and it'll all compile and work great:有了它,您可以编写以下内容,它会全部编译并运行良好:

public class ND4JDense extends BaseMatrix<ND4JMatrix> {
    ...
    // no need to write a getData method here at all!
    ...
}

ND4JDense dense = new ND4JDense();
ND4JMatrix matrix = dense.getData();

But, clearly, this makes no sense if you intend for the ND4JMatrix to remain an implementation detail that users of the BaseMatrix API should probably not be touching.但是,很明显,如果您打算让 ND4JMatrix 保留 BaseMatrix API 的用户可能不应该接触的实现细节,这显然是没有意义的。


EDIT: You changed the question on me, later.编辑:你后来改变了我的问题。 Now you want the mmul method to take 'self' as argument, effectively: You want the same type to be passed in.现在您希望mmul方法有效地将“self”作为参数:您希望传入相同的类型。

You can sort of do that but it is a little tricky.你可以这样做,但这有点棘手。 You need the self-ref generics hack.您需要自我参考 generics hack。 It looks like this:它看起来像这样:

public class BaseMatrix<T extends BaseMatrix<T>> {
   public abstract T mmul(T other);
}

In practice the only valid value for T is your own class, or at least, that is the intent.在实践中,T 的唯一有效值是您自己的 class,或者至少,这是意图。 This works fine:这工作正常:

public class ND4JDenseMatrix extends BaseMatrix<ND4JDenseMatrix> {
    public ND4JDenseMatrix mmul(ND4JDenseMatrix other) {
      .. impl here ..
    }
}

As far as I see, you have two issues in your code:据我所知,您的代码中有两个问题:

  1. You're not actually overriding the method of the superclass.您实际上并没有覆盖超类的方法。 What you have created is an overload of method mmul .您创建的是方法mmul的重载。 To correctly override the method, the method signature must match, in particular the input parameter must be the same.要正确覆盖方法,方法签名必须匹配,特别是输入参数必须相同。 It's ok to have a subtype of the return type, as Java support covariant .可以有返回类型的子类型,因为 Java 支持covariant If you instead put one of its subclass, that is overloading.如果您改为放置其子类之一,那就是重载。 Hope you get the difference.希望你能有所作为。 So the correct signature can be the following:所以正确的签名可以是:

    public BaseMatrix mmul(BaseMatrix other) {...公共 BaseMatrix mmul(BaseMatrix other) {...
    } }

  2. You have not specified the type T , so the compiler cannot know that by assumption is a subtype of BaseMatrix .您尚未指定类型T ,因此编译器无法知道假设是BaseMatrix的子类型。 it can be any type, even Object for example, so you are going to get "method not found" compilation error.它可以是任何类型,例如Object ,所以你会得到“找不到方法”的编译错误。

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

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