如何正确返回从类扩展并实现接口的内容?

时间:2013-10-30 09:09:15

标签: java class interface extends implements

背景

这个问题适用于Java和Android开发人员。

在Android上,我希望有一个函数可以返回从View扩展并实现Checkable的内容,这两个函数都是Android API的一部分。

我尝试了什么

在这里和互联网上的其他地方搜索了一下之后,我找到了这个解决方案:

private <T extends View & Checkable> T get() {
    return (T) mView;
}

请注意,mView是扩展View的任何类型(并且有很多),但我保证它实现了Checkable,因为只有那些类型可以在那里。

这显示“类型安全:未经检查从视图转换为T”的警告,但这不是问题。 问题是我无法正确使用此方法,例如:

View v=get(); //this works fine
Checkable c=get(); // this shows error

第二行显示错误:

  

绑定不匹配:类型为...的泛型方法get()不是   适用于arguments()。推断类型Checkable&amp; View是   不是有界参数的有效替代

所以我尝试使用“,”代替“&amp;” 。同样的错误。

然而,当我使用“,”并反转顺序时,我收到一个警告“类型参数View隐藏了类型View”,两行都可以正常工作。

这是代码:

private <T extends Checkable, View> T get() {
    return null;
}

private void foo() {
    final View v = get(); // this works fine
    final Checkable c = get();// this works fine too
}

但是,当我将返回的类型发送给其他函数时,它们不会将类型本身作为扩展类和接口的东西,并且需要一直投射(到View)。

问题

这样做的正确方法是什么?

如果我展示的最后一种方式是正确的,为什么我会收到警告呢?如果总有一个类最多可以扩展,为什么顺序很重要?

3 个答案:

答案 0 :(得分:4)

在我深入了解仿制药之前.... 你为什么不引入CheckableView并返回该类型?

public class CheckableView extends View implements Checkable {
}


 private CheckableView getCheckableView() {
     return checkableView;
 }

如果View界面,则有效:

interface View {}
interface Checkable {}
class CheckableView implements Checkable, View {}

public class Main {

    private static CheckableView checkableView;

    private static  <T extends View & Checkable> T get() {
        return (T) checkableView;
    }

    public static void main(String[] args) {
        View view = Main.get();
        Checkable checkable = Main.get();
    }
}

我认为理解泛型的问题在于,T extends View & Checkable在实际案例中并不意味着您可以返回任何扩展View并实现Checkable的类型。

这意味着客户端可以将返回的类型强制转换为扩展View并实现Checkable的任何类型。

客户可以这样做:

class CheckableView extends View implements Checkable {}
class OtherView extends View implements Checkable {}

private static  <T extends View & Checkable> T get() {
    return (T) checkableView;
}

OtherView otherView = Main.get();
CheckableView checkableView = Main.get();

但是你现在想要实现什么?

 private <T extends View & Checkable> T get() {
      return ...;
 }

这种方法应该返回什么?如果您返回CheckableView,编译器会说您必须将其强制转换为T,因为客户端可能会将其强制转换为任何类型T。但这也意味着您将从CheckableView到T 警告获得未经检查的强制转换,因为如果客户端将其转换为除CheckableView以外的任何其他类型,例如OhterView,您将获得ClassCastException

由于get方法实现者无法知道客户端将它投射到哪种类型,因此无法确定要返回的对象。

如果实现者返回特定对象(例如CheckableView),则客户端只能将其强制转换为该类型。那么为什么实现者不会更改方法签名以返回该类型呢? ....或为此案例引入特殊类型或界面?

也许这是一个适合您的解决方案

创建get方法将返回的界面,例如

public interface CheckableView {
}

更改get方法以返回CheckableView

private CheckableView get() {
}

编写一个适配器,使View实施Checkable的{​​{1}}适应CheckableView

public class CheckableViewAdapter<T extends View & Checkable> implements CheckableView {

    private T checkableView;

    public  CheckableViewAdapter(T checkableView) {
        this.checkableView = checkableView;
    }
}

现在,您可以为实现CheckableView的每个View创建Checkable的实例

class SomeView extends View implements Checkable {}

SomeView someView = ...;
CheckableView checkableView = new CheckableViewAdapter<SomeView>(someView);

get方法的客户端所需的方法添加到CheckableView接口,并在CheckableViewAdapter中实现。

public interface CheckableView {
     public void bringToFront();
}

public class CheckableViewAdapter<T extends View & Checkable> implements CheckableView {

    private T checkableView;

    public  CheckableViewAdapter(T checkableView) {
        this.checkableView = checkableView;
    }

        public void bringToFront(){
            checkableView.bringToFront();
        }
}

或只是

public interface CheckableView {
     public View getView();
     public Checkable getCheckable();
}

答案 1 :(得分:1)

您收到错误,因为第二个呼叫站点参数T的推断类型,即Checkable c = get(),只是Checkable,因为错误消息正确报告。

如果强制调用使用兼容类型View & Checkable,它将编译。为此,您必须明确提供类型,例如:

private <T extends View & Checkable> T get() {
   return (T) mView;
}

private <T extends View & Checkable> void useGeneric() {
    View v = this.get(); // no need for an explicit type
    Checkable c = this.<T>get(); // an explicit type needed
    T t = this.get(); // best way
}

private void useSpecific() {
    class Specific extends View implements Checkable {}
    Checkable c = this.<Specific>get(); // risk of ClassCastException
    Specific s = this.get(); // best way; risk of ClassCastException
}

在分配给类的情况下,它不需要显式类型,而在接口情况下需要一个显式类型,在我看来就像类型推理算法工件,类/接口不对称。< / p>

答案 2 :(得分:0)

问题是你有逗号而不是&符号!这个定义:

private <T extends Checkable, View> T get() {

一个十字路口!相反,它声明了2个泛型类型,一个名为T,另一个名为View,未在您的类中使用 - 它与编码相同:

private <T extends Checkable, V> T get() {

所以,将其改为:

private <T extends View & Checkable> T get() {

注意通用交叉点,该类必须位于接口之前。