簡體   English   中英

如何在基類中創建Java泛型

[英]How to create Java generic in base class

編輯

基於討論,我將SystemUnderTestFactory實現更改為此 (以及測試

public abstract class BaseUnitTesterWithSut<TSut>
{
    protected SystemUnderTestFactory<TSut> SutFactory;

    @Before
    @SuppressWarnings("unchecked")
    public void before()
    {
        ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
        SutFactory = new SystemUnderTestFactory<>((Class<TSut>) type.getActualTypeArguments()[0]);
    }
}

public class SystemTester extends BaseUnitTesterWithSut<System>
{
    @Test
    public void can_subclass_and_test()
    {
        System sut = SutFactory.sut();
        assertThat(sut).isExactlyInstanceOf(System.class);
    }
}

原始問題

背景:我是一名C#開發人員,剛開始從事Java 1.8項目,並試圖了解在Java中使用泛型時如何完成事情(對我而言,泛型實現和整個類型擦除使它們幾乎毫無意義)。

我想做的是創建一個具有通用字段(“屬性”)的基類,該字段在基類中實例化並在子類中使用。

另外,我正在創建的屬性是抽象類的匿名子類,因此我可以使用超類信息在運行時使用此“技巧”來檢查泛型(這是一個大問題)

我有什么辦法可以完成下面要執行的操作而不必在基類中進行方法重寫? 在使用Java時,我只是在以錯誤的方式考慮泛型嗎? 同樣,我習慣於使用C#來做事情,因此,如果有一種Java的做事方式需要學習/切換,那么我也會對此感興趣。

我想完成的

public abstract class BaseUnitTesterWithSut<TSut>
{
    protected SystemUnderTestFactory<TSut> SutFactory;

    @Before
    public void before()
    {
        SutFactory = new SystemUnderTestFactory<TSut>() { };
    }
}

public class SystemTester extends BaseUnitTesterWithSut<System>
{
    @Test
    public void can_subclass_and_test()
    {
        System sut = SutFactory.sut();
        assertThat(sut).isExactlyInstanceOf(System.class);
    }
}

我的解決方法

public class SystemTester // extends BaseUnitTesterWithSut<System>
{
    protected SystemUnderTestFactory<System> SutFactory;

    @Before
    public void before()
    {
        SutFactory = new SystemUnderTestFactory<System>() { }
    }

    @Test
    public void can_subclass_and_test()
    {
        System sut = SutFactory.sut();
        assertThat(sut).isExactlyInstanceOf(System.class);
    }
}

SystemUnderTestFactory的縮寫版本

它為依賴關系最多的構造函數檢查一個類,並為每個依賴關系自動創建模擬,當您檢索該對象時,它將使用該構造函數及其創建的模擬來創建您的對象。

如果您有興趣,可以在此處查看完整的實現(以及測試

public abstract class SystemUnderTestFactory<TSut>
{
    private final Class<TSut> _type;
    private volatile Constructor<?> _ctor;

    private ArrayList _dependencies;
    private TSut _sut;

    public SystemUnderTestFactory()
    {
        ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass();
        _type = (Class<TSut>) superclass.getActualTypeArguments()[0];

        _dependencies = new ArrayList();
        _ctor = getGreediestCtor();

        Class[] parameterTypes = _ctor.getParameterTypes();
        for (Class cls : parameterTypes)
        {
            // Mockito mock
            _dependencies.add(mock(cls));
        }
    }

    public TSut sut()
    {
        return (TSut) _ctor.newInstance(_dependencies.toArray());;
    }
}

它看起來像你可以得到從反射類BaseUnitTesterWithSut而不是SystemUnderTestFactory ,再通入。

abstract class BaseUnitTesterWithSut<TSut>
{
    protected SystemUnderTestFactory<TSut> SutFactory;

    @Before
    public void before()
    {
        ParameterizedType thisType = (ParameterizedType) getClass().getGenericSuperclass();
        SutFactory = new SystemUnderTestFactory<>((Class<TSut>) thisType.getActualTypeArguments()[0]);
    }
}

我也有些困惑。 我認為您在基本語法方面沒有問題,但是我也沒有真正看到該問題。 這是一些編譯的代碼。 它不會運行,我也不必費心獲取type參數。 我假設您正在運行。

如果不對此代碼發表評論,我不知道如何提出問題,所以我將冒一定的聲譽並將其發布。 讓我知道您認為不正確/無法實施的部分。

我已經更改了一些方法和字段名稱,使其更像Java,但除此之外,前兩個類直接來自您的問題。

abstract class BaseUnitTesterWithSut<TSut>
{
    protected SystemUnderTestFactory<TSut> sutFactory;

    @Before
    public void before()
    {
        sutFactory = new SystemUnderTestFactory<TSut>() { };
    }
}

class SystemTester extends BaseUnitTesterWithSut<MySystem>
{
    @Test
    public void can_subclass_and_test()
    {
        MySystem sut = sutFactory.getSut();
        assert( sut.getClass() == MySystem.class );
    }
}

class SystemUnderTestFactory<T> {
   T getSut() { return null; }
}

class MySystem {}
@interface Before {}
@interface Test {}

這樣做很合適,那么您可能會完全擺脫抽象基類,而直接使用工廠加載類。 我認為您可能已經使問題復雜化了。

我專注於getSut()代碼。 你需要放什么? 它實際上在哪里找到這些類? 我認為您正在關注泛型,但是Java中可能有更好的方法來實現此目的。

這里的問題是您使用Java泛型的方式並不完全是其主要目的。

實際上,在Java中,泛型在這里主要是在編譯時確保類型安全,並避免一些煩人的強制轉換等。由於類型擦除,它們並不是真正在運行時用於動態創建對象。

為了在運行時創建實例,我們通常將Class對象傳遞給構造函數或方法,以為其提供運行時類型信息。

在這里,在目標代碼中似乎不太可能,但是我真的看不到將那段小代碼提取到基類中會得到什么。 我知道下面沒有解決關於編寫像你最初的問題,而是什么:

public class SystemTester {

    private SystemUnderTestFactory<System> sutFactory;

    @Before
    public void before() {
        sutFactory = new SystemUnderTestFactory<System>(System.class);
    }

    @Test
    public void canSubclassAndTest() {
        System sut = sutFactory.sut();
        assertThat(sut).isExactlyInstanceOf(System.class);
    }
}

使用SystemUnderTestFactory的構造函數的簡化版本:

public SystemUnderTestFactory(Class<TSut> type) {
    _type = type;

    _dependencies = new ArrayList();
    _ctor = getGreediestCtor();

    Class[] parameterTypes = _ctor.getParameterTypes();
    for (Class cls : parameterTypes)
    {
        // Mockito mock
        _dependencies.add(mock(cls));
    }
}

暫無
暫無

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

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