[英]Proper use of generics in abstract java class?
編輯:這個問題措辭不好,提供的答案在字面上是正確的,但沒有教我如何獲得我需要的東西。 如果您遇到同樣的問題,這最終幫助了我: 當這些方法的返回類型取決於子 class 時,如何強制執行子 class 行為/方法?
我正在嘗試從我寫的樣板抽象 class 中實現一個基本矩陣 class 。 這個抽象 class 將有幾個實現,每個都使用不同的數學庫,然后我將測試速度。
每個實現都將其數據保存在該庫的本機矩陣數據結構中。 我認為這是 generics 的用例。 在這一點上,我認為我已經閱讀了太多教程並觀看了太多視頻,因為我似乎無法找出所有正確的位置來放置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.
將其數據保存在該庫的本機矩陣數據結構中。 我認為這是 generics 的用例。
Generics 用於鏈接事物。 您使用<T>
聲明了類型變量,並且就您的粘貼而言,您已經在一個地方(T 類型的字段)中使用了它。 這是一面紅旗; 一般來說,鑒於它鏈接事物,如果你只在一個地方使用它,這通常是一個不好的跡象。
這就是我的意思:想象你想寫一個方法說:這個方法接受 2 個參數並返回一些東西。 這段代碼並不特別關心你在這里折騰什么,但是,參數必須是相同的類型,我也返回該類型的東西。 您希望將參數的類型、另一個參數的類型和返回類型鏈接在一起。
這就是 generics 的用途。
如果我們稍微扭轉一下想法,它可能適用於此:您希望將data
字段的類型與 BaseMatrix 的某些特定實現只能對某些特定類型(例如 ND4JMatrix)進行操作的概念聯系起來。
但是,大多數情況下,不,這並沒有讓我覺得正確使用 generics。 你可以很容易地完全避免它:只是......停止擁有private T data;
場地。 你在這里有什么好處? 你不知道那是什么類型,你甚至不知道它是否是可序列化的。 你對此一無所知,編譯器證實了這一點:你絕對不能用 object 做任何事情,除了你可以對所有通常很無趣的對象做的事情。 您可以在其上調用.toString()
,對其進行同步,或者調用.hashCode()
,僅此而已。
為什么不直接放棄那個領域? 實現可以使字段,不需要它在基地!
public class ND4JDense extends BaseMatrix {
private ND4JMatrix data; // why not like this?
}
(此代碼假定 'ND4JMatrix' 是您在此處所需的正確數據類型,可以是 ND4J impl 中數據的內部表示)。
但是,如果必須,是的,您可以在此處使用 generics。 您已經鍵入了變量 BaseMatrix,這意味着BaseMatrix 的所有用法都必須參數化。 那是您在代碼中搞砸的部分。 如果我們 go 與您的類型參數化 BaseMatrix class 和類型 T 字段的計划,正確的代碼是:
public class ND4JDense extends BaseMatrix<ND4JMatrix> {
...
}
但是,我不會這樣做(我希望 go 擁有該領域,簡單得多,無需用泛型打擾任何人)。 當然,除非您確實需要該字段並且它是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”作為參數:您希望傳入相同的類型。
你可以這樣做,但這有點棘手。 您需要自我參考 generics 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
的重載。 要正確覆蓋方法,方法簽名必須匹配,特別是輸入參數必須相同。 可以有返回類型的子類型,因為 Java 支持covariant
。 如果您改為放置其子類之一,那就是重載。 希望你能有所作為。 所以正確的簽名可以是:
公共 BaseMatrix mmul(BaseMatrix other) {...
}
您尚未指定類型T
,因此編譯器無法知道假設是BaseMatrix
的子類型。 它可以是任何類型,例如Object
,所以你會得到“找不到方法”的編譯錯誤。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.