在抽象中正确使用泛型 java class?
Proper use of generics in abstract java class?
编辑:这个问题措辞不当,提供的答案在字面意义上是正确的,但没有教我如何获得我需要的东西。如果您正在为同样的问题而苦苦挣扎,这就是最终帮助我的方法:
我正在尝试根据我编写的样板摘要 class 实现基本矩阵 class。这个抽象将有几个实现 class,每个都使用不同的数学库,然后我将测试它们的速度。
每个实现都将其数据保存在该库的本机矩阵数据结构中。我认为这是泛型的一个用例。在这一点上,我认为我已经阅读了太多的教程和观看了太多的视频,因为我似乎无法找出放置 T
符号的所有正确位置以使其正常工作。
所以我的问题是双重的:
- 我是否误用或错过了泛型的要点?
- 如果不是,使用它们的正确语法是什么?
我已经阅读了文档和三个不同的教程,但仍然无法理解。
这是我试过的方法:
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);
这是我的实现:
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;
}
错误是: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.
泛型服务于 link 事物。您使用 <T>
声明了类型变量,并且就您的粘贴而言,您已经在一个地方(字段,T 类型)使用了它。那是一个危险信号;一般来说,考虑到它 link 的东西,如果你只在一个地方使用它通常是一个不好的迹象。
这就是我的意思:想象一下你想写一个方法说:这个方法有 2 个参数和 returns 东西。这段代码并不特别关心你在这里扔什么,但是,参数必须是相同的类型,我 return 也是那种类型的东西。你想 link 参数的类型,另一个参数的类型,和 return 类型在一起。
就是泛型的用途。
它可能适用于此,如果我们稍微扭曲一下想法:你想 link data
字段的类型到一个概念,即 BaseMatrix 的某些特定实现只能对某些特定类型,例如ND4J矩阵.
但是,大多数情况下,不,这并没有让我觉得这是对泛型的正确使用。您可以很容易地完全避免它:只需.. 停止使用 private T data;
字段。你在这里有什么好处?您不知道 那是什么类型,您甚至不知道它是否可序列化。你对此一无所知,编译器证实了这一点:绝对没有你可以对那个对象做的事情,除了你可以对所有对象做的事情,这些事情通常是非常无趣的。您可以对其调用 .toString()
,对其进行同步,也许调用 .hashCode()
,仅此而已。
为什么不放弃那个领域呢?实现可以制作字段,不需要在base中!
public class ND4JDense extends BaseMatrix {
private ND4JMatrix data; // why not like this?
}
(此代码假设 'ND4JMatrix' 是您在这里想要的正确数据类型,可以是 ND4J impl 中数据的内部表示)。
但是,如果必须,可以在这里使用泛型。您已经对 BaseMatrix 进行了类型转换,这意味着 BaseMatrix 的所有用法都必须参数化。那是您在代码中搞砸的部分。如果我们按照您的类型参数化 BaseMatrix class 和类型 T 字段的计划进行,正确的代码是:
public class ND4JDense extends BaseMatrix<ND4JMatrix> {
...
}
但是,我不会这样做(我会选择让 impl 拥有字段,更简单,不需要用泛型打扰任何人)。当然,除非您 DO 确实需要该字段并且它是 BaseMatrix 的 API 的一部分。例如,如果您想要这样:
public class BaseMatrix<T> {
public T getData() { return data; }
}
然后它开始变得更有意义了。有了它,您可以编写以下内容,它们都可以编译并运行良好:
public class ND4JDense extends BaseMatrix<ND4JMatrix> {
...
// no need to write a getData method here at all!
...
}
ND4JDense dense = new ND4JDense();
ND4JMatrix matrix = dense.getData();
但是,很明显,如果您打算让 ND4JMatrix 保留 BaseMatrix API 的用户可能不应该接触的实现细节,这就没有意义了。
编辑:您稍后更改了我的问题。现在您希望 mmul
方法将 'self' 作为参数,有效地:您希望传入相同的类型。
您可以这样做,但这有点棘手。您需要自引用泛型 hack。它看起来像这样:
public class BaseMatrix<T extends BaseMatrix<T>> {
public abstract T mmul(T other);
}
实际上,T 的唯一有效值是您自己的 class,或者至少,这是意图。这很好用:
public class ND4JDenseMatrix extends BaseMatrix<ND4JDenseMatrix> {
public ND4JDenseMatrix mmul(ND4JDenseMatrix other) {
.. impl here ..
}
}
据我所知,您的代码中有两个问题:
您实际上并没有覆盖超类的方法。您创建的是方法 mmul
的重载。要正确覆盖方法,方法签名必须匹配,特别是输入参数必须相同。可以有 return 类型的子类型,因为 Java 支持 covariant
。如果你改为放置它的子类之一,那就是重载。希望你能有所不同。所以正确的签名可以是:
public BaseMatrix mmul(BaseMatrix other) {
...
}
您没有指定类型 T
,因此编译器无法根据假设知道它是 BaseMatrix
的子类型。它可以是任何类型,例如 Object
,因此您将得到“找不到方法”编译错误。
编辑:这个问题措辞不当,提供的答案在字面意义上是正确的,但没有教我如何获得我需要的东西。如果您正在为同样的问题而苦苦挣扎,这就是最终帮助我的方法:
我正在尝试根据我编写的样板摘要 class 实现基本矩阵 class。这个抽象将有几个实现 class,每个都使用不同的数学库,然后我将测试它们的速度。
每个实现都将其数据保存在该库的本机矩阵数据结构中。我认为这是泛型的一个用例。在这一点上,我认为我已经阅读了太多的教程和观看了太多的视频,因为我似乎无法找出放置 T
符号的所有正确位置以使其正常工作。
所以我的问题是双重的:
- 我是否误用或错过了泛型的要点?
- 如果不是,使用它们的正确语法是什么?
我已经阅读了文档和三个不同的教程,但仍然无法理解。
这是我试过的方法:
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);
这是我的实现:
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;
}
错误是: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.
泛型服务于 link 事物。您使用 <T>
声明了类型变量,并且就您的粘贴而言,您已经在一个地方(字段,T 类型)使用了它。那是一个危险信号;一般来说,考虑到它 link 的东西,如果你只在一个地方使用它通常是一个不好的迹象。
这就是我的意思:想象一下你想写一个方法说:这个方法有 2 个参数和 returns 东西。这段代码并不特别关心你在这里扔什么,但是,参数必须是相同的类型,我 return 也是那种类型的东西。你想 link 参数的类型,另一个参数的类型,和 return 类型在一起。
就是泛型的用途。
它可能适用于此,如果我们稍微扭曲一下想法:你想 link data
字段的类型到一个概念,即 BaseMatrix 的某些特定实现只能对某些特定类型,例如ND4J矩阵.
但是,大多数情况下,不,这并没有让我觉得这是对泛型的正确使用。您可以很容易地完全避免它:只需.. 停止使用 private T data;
字段。你在这里有什么好处?您不知道 那是什么类型,您甚至不知道它是否可序列化。你对此一无所知,编译器证实了这一点:绝对没有你可以对那个对象做的事情,除了你可以对所有对象做的事情,这些事情通常是非常无趣的。您可以对其调用 .toString()
,对其进行同步,也许调用 .hashCode()
,仅此而已。
为什么不放弃那个领域呢?实现可以制作字段,不需要在base中!
public class ND4JDense extends BaseMatrix {
private ND4JMatrix data; // why not like this?
}
(此代码假设 'ND4JMatrix' 是您在这里想要的正确数据类型,可以是 ND4J impl 中数据的内部表示)。
但是,如果必须,可以在这里使用泛型。您已经对 BaseMatrix 进行了类型转换,这意味着 BaseMatrix 的所有用法都必须参数化。那是您在代码中搞砸的部分。如果我们按照您的类型参数化 BaseMatrix class 和类型 T 字段的计划进行,正确的代码是:
public class ND4JDense extends BaseMatrix<ND4JMatrix> {
...
}
但是,我不会这样做(我会选择让 impl 拥有字段,更简单,不需要用泛型打扰任何人)。当然,除非您 DO 确实需要该字段并且它是 BaseMatrix 的 API 的一部分。例如,如果您想要这样:
public class BaseMatrix<T> {
public T getData() { return data; }
}
然后它开始变得更有意义了。有了它,您可以编写以下内容,它们都可以编译并运行良好:
public class ND4JDense extends BaseMatrix<ND4JMatrix> {
...
// no need to write a getData method here at all!
...
}
ND4JDense dense = new ND4JDense();
ND4JMatrix matrix = dense.getData();
但是,很明显,如果您打算让 ND4JMatrix 保留 BaseMatrix API 的用户可能不应该接触的实现细节,这就没有意义了。
编辑:您稍后更改了我的问题。现在您希望 mmul
方法将 'self' 作为参数,有效地:您希望传入相同的类型。
您可以这样做,但这有点棘手。您需要自引用泛型 hack。它看起来像这样:
public class BaseMatrix<T extends BaseMatrix<T>> {
public abstract T mmul(T other);
}
实际上,T 的唯一有效值是您自己的 class,或者至少,这是意图。这很好用:
public class ND4JDenseMatrix extends BaseMatrix<ND4JDenseMatrix> {
public ND4JDenseMatrix mmul(ND4JDenseMatrix other) {
.. impl here ..
}
}
据我所知,您的代码中有两个问题:
您实际上并没有覆盖超类的方法。您创建的是方法
mmul
的重载。要正确覆盖方法,方法签名必须匹配,特别是输入参数必须相同。可以有 return 类型的子类型,因为 Java 支持covariant
。如果你改为放置它的子类之一,那就是重载。希望你能有所不同。所以正确的签名可以是:public BaseMatrix mmul(BaseMatrix other) { ...
}您没有指定类型
T
,因此编译器无法根据假设知道它是BaseMatrix
的子类型。它可以是任何类型,例如Object
,因此您将得到“找不到方法”编译错误。