如何在基类

时间:2015-07-15 17:21:16

标签: java generics

修改

根据讨论,我已将SystemUnderTestFactory实现更改为this(以及tests

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);
    }
}

原始问题

背景:我是一个刚刚开始研究Java 1.8项目的C#开发人员,我正在尝试理解如何在使用Java中的Generics时完成任务(对我来说,Generic实现和整个类型的擦除事情使它们几乎毫无意义)。

我要做的是创建一个带有Generic字段(“property”)的基类,该字段在基类中实例化并在子类中使用。

此外,我正在创建的属性是抽象类的匿名子类,因此我可以使用超类信息在运行时使用this "trick"检查泛型类型(这是一口)

有没有办法在不必在基类中执行方法覆盖的情况下完成我想要做的事情?我是否只是在使用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

的缩写版本

它检查具有最多依赖项的构造函数的类,并为每个依赖项自动创建模拟,当您检索该对象时,它将使用该构造函数及其创建的模拟创建一个。

如果您有兴趣可以看到完整的实施here(以及tests

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());;
    }
}

3 个答案:

答案 0 :(得分:2)

看起来您可以使用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]);
    }
}

答案 1 :(得分:1)

我也有点困惑。我不认为你的基本语法有问题,但我也没有真正看到这个问题。这是一些编译的代码。它没有运行,我也不用费心去获取类型参数。我猜你有点跑。

我不知道如何在没有此代码的情况下提出问题进行评论,因此我会冒一些声誉并发布此信息。让我知道您认为哪些部分不正确/无法实施。

我已经将某些方法和字段名称更改为类似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可能有更好的方法来实现这一点。

答案 2 :(得分:1)

这里的问题是你使用Java泛型的方式并不完全是他们的主要目的。

事实上,在Java中,泛型在这里主要是为了确保编译时的类型安全,并避免一些恼人的转换等。由于类型擦除,它们实际上并不意味着在运行时用于动态创建对象。 / p>

要在运行时创建实例,我们通常会将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));
    }
}