java - 在抽象 java 类中正确使用泛型?
问题描述
编辑:这个问题措辞不好,提供的答案在字面上是正确的,但没有教我如何获得我需要的东西。如果您正在为同样的问题苦苦挣扎,这最终帮助了我:当这些方法的返回类型取决于子类时,如何强制执行子类行为/方法?
我正在尝试从我编写的样板抽象类中实现一个基本的矩阵类。这个抽象类将有几个实现,每个都使用不同的数学库,然后我将测试它的速度。
每个实现都将其数据保存在该库的本机矩阵数据结构中。我认为这是泛型的一个用例。在这一点上,我认为我已经阅读了太多教程并观看了太多视频,因为我似乎无法找出所有正确的位置来放置T
Notation 以使其正常工作。
所以我的问题是双重的:
- 我是否误用或错过了泛型的意义?
- 如果不是,它们使用的正确语法是什么?
我已经阅读了文档以及关于三个不同教程的内容,但仍然无法理解。
这是我尝试过的:
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.
解决方案
将其数据保存在该库的本机矩阵数据结构中。我认为这是泛型的一个用例。
泛型用于链接事物。您用 声明了类型变量<T>
,并且就您的粘贴而言,您已经在一个位置(一个字段,T 类型)中使用了它。这是一面红旗;一般来说,鉴于它链接事物,如果你只在一个地方使用它,这通常是一个不好的迹象。
这就是我的意思:想象你想写一个方法说:这个方法接受 2 个参数并返回一些东西。这段代码并不特别关心你在这里折腾什么,但是,参数必须是相同的类型,我也返回该类型的东西。您希望将参数的类型、另一个参数的类型和返回类型链接在一起。
这就是泛型的用途。
如果我们稍微扭转一下思路,它可能适用于此:您希望将data
字段的类型与 BaseMatrix 的某些特定实现只能对某些特定类型(例如 ND4JMatrix)进行操作的概念联系起来。
然而,大多数情况下,不,这并没有让我觉得正确使用泛型。你可以很容易地完全避免它:只是......停止拥有那个private T data;
领域。你在这里有什么好处?你不知道那是什么类型,你甚至不知道它是否是可序列化的。你对此一无所知,编译器证实了这一点:你绝对不能对那个对象做任何事情,除了你可以对所有通常很无趣的对象做的事情。你可以调用.toString()
它,同步它,也许调用它.hashCode()
,就是这样。
为什么不直接放弃那个领域?实现可以使字段,不需要它在基地!
public class ND4JDense extends BaseMatrix {
private ND4JMatrix data; // why not like this?
}
(此代码假定 'ND4JMatrix' 是您在此处所需的正确数据类型,可以是 ND4J impl 中数据的内部表示)。
但是,如果必须,是的,您可以在此处使用泛型。您已经键入了变量 BaseMatrix,这意味着BaseMatrix 的所有用法都必须参数化。那是您在代码中搞砸的部分。如果我们按照您的计划进行类型参数化 BaseMatrix 类和类型 T 的字段,则正确的代码是:
public class ND4JDense extends BaseMatrix<ND4JMatrix> {
...
}
但是,我不会这样做(我会让 impl 拥有该字段,更简单,无需用泛型打扰任何人)。当然,除非您确实需要该字段并且它是 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”作为参数:您希望传入相同的类型。
你可以这样做,但这有点棘手。您需要自引用泛型破解。它看起来像这样:
public class BaseMatrix<T extends BaseMatrix<T>> {
public abstract T mmul(T other);
}
实际上, T 的唯一有效值是您自己的类,或者至少是意图。这工作正常:
public class ND4JDenseMatrix extends BaseMatrix<ND4JDenseMatrix> {
public ND4JDenseMatrix mmul(ND4JDenseMatrix other) {
.. impl here ..
}
}
推荐阅读
- stream - Dart Streams API(使用 rxdart)与其他响应式库(如 RxJava 和 RxJS)之间的主要区别是什么?
- javascript - 如何获取 JavaScript 代理的目标?
- python - 最小二乘法
- mysql - Mysql查询以获取特定月份有bday的所有用户
- python - 我怎么能说在一行中检查“如果 a_list 为空或 a_list[0].property == something”?
- java - 如何在 GWT 中使用 FormBuilder
- sql - SQL SERVER exec 存储过程到 CASE WHEN 条件
- pandas - 如何对熊猫进行模查询
- javascript - 如何像在移动设备上一样垂直制作 React-semantic-ui 下拉菜单?
- python - 在tensorflow中动态提取N维张量的N-1维张量