簡體   English   中英

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

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

編輯:這個問題措辭不好,提供的答案在字面上是正確的,但沒有教我如何獲得我需要的東西。 如果您遇到同樣的問題,這最終幫助了我: 當這些方法的返回類型取決於子 class 時,如何強制執行子 class 行為/方法?

我正在嘗試從我寫的樣板抽象 class 中實現一個基本矩陣 class 。 這個抽象 class 將有幾個實現,每個都使用不同的數學庫,然后我將測試速度。

每個實現都將其數據保存在該庫的本機矩陣數據結構中。 認為這是 generics 的用例。 在這一點上,我認為我已經閱讀了太多教程並觀看了太多視頻,因為我似乎無法找出所有正確的位置來放置T表示法以使其正常工作。

所以我的問題是雙重的:

  1. 我是否誤用或錯過了 generics 的要點?
  2. 如果不是,它們使用的正確語法是什么?

我已經閱讀了文檔以及關於三個不同教程的內容,但仍然無法理解。

這是我嘗試過的:

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

據我所知,您的代碼中有兩個問題:

  1. 您實際上並沒有覆蓋超類的方法。 您創建的是方法mmul的重載。 要正確覆蓋方法,方法簽名必須匹配,特別是輸入參數必須相同。 可以有返回類型的子類型,因為 Java 支持covariant 如果您改為放置其子類之一,那就是重載。 希望你能有所作為。 所以正確的簽名可以是:

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

  2. 您尚未指定類型T ,因此編譯器無法知道假設是BaseMatrix的子類型。 它可以是任何類型,例如Object ,所以你會得到“找不到方法”的編譯錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM