简体   繁体   English

理解泛型:当类具有泛型类型并实现其参数化超类之一时,不兼容的类型

[英]Understanding generics: Incompatible types when class has generic type and implements one of its parametrised superclass

I am implementing a simple app with MVP architecture. 我正在使用MVP架构实现一个简单的应用程序。

Here are my MvpView and MvpPresenter interfaces (nothing interesting about MvpModel , so I am skipping it): 这里是我的MvpViewMvpPresenter接口(没有什么有趣的MvpModel ,所以我跳过吧):

/// MvpView.java
public interface MvpView {
}

/// MvpPresenter.java
public interface MvpPresenter<V extends MvpView> {
    void attachView(V view);

    void detachView();
}

Now I have a basic MvpView implementation, which is an Activity : 现在我有一个基本的MvpView实现,这是一个Activity

// BaseActivity.java
public abstract class BaseActivity<V extends MvpView, P extends MvpPresenter<V>> 
        extends AppCompatActivity implements MvpView {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getPresenter().attachView(this);
    }

    public abstract P getPresenter();

    // other logic
}

As for me everything looks correct, but there is a compile error in line: 至于我,一切看起来都正确,但是有一个编译错误:

    getPresenter().attachView(this);

不兼容的类型

If I add cast to V then project compiles and everything works fine: 如果我将演员表添加到V然后项目编译,一切正常:

    getPresenter().attachView((V) this);

1. The question is why I need this cast or why I am experiencing this incompatible types error without casting? 1.问题是为什么我需要这个演员为什么我在没有演员的情况下遇到这种不兼容的类型错误?

(1 is already answered by Eran ) (1已经由伊兰回答)

2. How I can link V to BaseActivity in this example or how it is better to implement this MVP approach? 2.如何在此示例BaseActivity V链接到BaseActivity或如何更好地实现此MVP方法?

It is strange, as for me, because my BaseActivity is extending MvpView as it is defined by this generic parameter: V extends MvpView ! 对我来说很奇怪,因为我的BaseActivity正在扩展MvpView因为它是由这个通用参数定义的: V extends MvpView

getPresenter() is an instance of type P , which extends MvpPresenter<V> . getPresenter()P类型的实例,它扩展了MvpPresenter<V> Therefore getPresenter.attachView() expects an argument of type V . 因此, getPresenter.attachView()需要一个V类型的参数。

Now, we know that V must implement MvpView , and we also know that BaseActivity implements MvpView , but this implementations don't necessarily match. 现在,我们知道V必须实现MvpView ,我们也知道BaseActivity实现了MvpView ,但是这个实现不一定匹配。

For example, you can create a concrete sub-class SubBaseActivity and instantiate it with: 例如,您可以创建一个具体的子类SubBaseActivity并使用以下实例化它:

SubBaseActivity<MvpViewImpl, MvpPresenterImpl<MvpViewImpl>>
    activity = new SubBaseActivity<> (); // let's ignore the fact that you are not suppose
                                         // to instantiate Android activities this way

Now getPresenter() returns a MvpPresenterImpl and getPresenter().attachView() expects an argument of type MvpViewImpl . 现在getPresenter()返回一个MvpPresenterImplgetPresenter().attachView()需要一个MvpViewImpl类型的参数。 But this is not of type MvpViewImpl . 但是, this 并不类型MvpViewImpl

When you make the unsafe cast from BaseActivity<V,P> to V , you are telling the compiler BaseActivity<V,P> can be cast to V . 当您从BaseActivity<V,P>V进行不安全的BaseActivity<V,P> ,您告诉编译器BaseActivity<V,P>可以转换为V However, the reason this works at runtime is that the compiler erases the generic type parameters V and P . 但是,这在运行时工作的原因是编译器擦除了泛型类型参数VP Since the type bound of V is MvpView , the casting to V becomes a casting to MvpView , which BaseActivity implements. 由于V的类型绑定是MvpView ,因此对V的转换成为MvpViewBaseActivity实现了它。

The problem is that you are assuming that V makes reference to BaseActivity<V,?> because you are probably using it only this way in practice. 问题是你假设V引用了BaseActivity<V,?>因为你可能只是在实践中使用它。

Unfortunately currently there is no way to make reference to the class you are declaring in the type arguments bounds as to force such a constraint. 不幸的是,目前没有办法引用你在类型参数边界中声明的类来强制这样的约束。

The simplest fix is to use an unchecked cast (masking the warning if you like): 最简单的解决方法是使用未经检查的强制转换(如果您愿意,可以屏蔽警告):

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    @SupressWarning("unchecked")
    final V myView = (V) this;
    getPresenter().attachView(myView);
}

You don't need to use the local myView variable but declaring it explicitly allows you to silence only that cast and not any other "unchecked" warning in that method. 您不需要使用本地myView变量,但明确声明它只允许您静音该方法而不是任何其他“未经检查”的警告。

In any case you must make sure that any extending class would set their V type-parameter to themselves as to not break contract: 在任何情况下,您必须确保任何扩展类都将其V类型参数设置为自己,以便不破坏合同:

public class ConcreteActivity<V extends MvpView, P extends MvpPresenter<V>> extends BaseActivity<ConcreteActivity<V,P>, P> {
...
} 

There is no way you can enforce that at compilation time, instead your test code should use reflection to verify that each extending class complies with such a restriction. 您无法在编译时强制执行此操作,而是您的测试代码应使用反射来验证每个扩展类是否符合此类限制。

You can go a bit further in order to avoid the unchecked cast warning but that would require for you to add a field typed V pointing to this set in the BasicActivity constructor passed as a parameter by the extending class constructor.... 您可以更进一步,以避免未经检查的强制转换警告,但这需要您在BasicActivity构造函数中添加一个字段类型V指向this集合,该构造函数由扩展类构造函数作为参数传递....

public abstract class BaseActivity<V extends MvpView, P extends MvpPresenter<V>> 
        extends AppCompatActivity implements MvpView {

    private final V myView;

    protected BaseActivity(final V myView) {
       if (myView != this) { throw new IllegalArgumentException("you must pass this object"); } 
       this.myView = myView;
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getPresenter().attachView(myView);
    }

    public abstract P getPresenter();

    // other logic
}


public class ConcreteActivity<V extends MvpView, P extends MvpPresenter<V> extends BaseActivity<BaseActivity<V, P>, P> {

    public ConcreteActivity() {
       super(this);
    }
    ...
}

Notice that we double check that myView is in fact this in the constructor in order to fail early if the extending class does not comply; 请注意,我们仔细检查myView是,其实this在构造函数,以便如果扩展的类不符合早期失效; you can leave that out if your test code makes sure that this is going to be always the case. 如果您的测试代码确保始终如此,您可以将其保留。

Despite that is preferable to avoid any kind of warning... I would say that in this situation the first alternative is quite acceptable as it needs less code and is more efficient memory-wise. 尽管如此,最好避免任何形式的警告...我会说在这种情况下,第一种选择是完全可以接受的,因为它需要更少的代码并且在内存方面更有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 不兼容的通用类型:使用带有泛型类型的.class时,无法指定子类型 - Incompatible Generic Types: Cannot specify sub-type when using .class with generic type Java泛型 - 了解将数组转换为泛型类中的泛型类型 - Java Generics - Understanding casting array to a generic type in a generic class 内部类泛型-不兼容的类型 - Inner class generics - Incompatible types 如何制作一个用两种泛型类型实现一个接口的 Java 类? - How to make a Java class that implements one interface with two generic types? 继承而不指定父类的泛型类型时,不兼容的类型错误 - Incompatible types error when inheriting without specifying generic type of parent class Java泛型:在一个集合类型中使用多个泛型类型 - Java Generics: Useing multiple generic types in one collection type 在接口中使用通用 class 列表时的不兼容类型 - Incompatible Types when when using a list of generic class in an interface Java Generics 不兼容类型为完全相同的 class - Java Generics incompatible types for the exact same class 返回泛型时不兼容的类型 - Incompatible types when returning a generic 当将此参数分配给实现该接口的对象时,如何使该接口作为此参数的类型不抛出“不兼容的类型”? - How to make this interface as this parameter's type not throw “incompatible types” when it's assigned to an object that implements the interface?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM