【问题标题】:How to Correctly return something that extends from a class and implements an interface?如何正确返回从类扩展并实现接口的东西?
【发布时间】:2013-11-09 18:36:12
【问题描述】:

背景

此问题适用于 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() 不是 适用于参数 ()。推断类型 Checkable&View 是 不是有界参数的有效替代品

所以我尝试使用 "," 而不是 "&" 。同样的错误。

但是,当我使用 "," 并反转 order 时,我收到警告 "The type parameter View is hidden type 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)。

问题

正确的做法是什么?

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

【问题讨论】:

  • mView 是你创建的类型吗?
  • @KevinBowersox 这不是问题。如果我愿意,我什至可以在那里返回 null。

标签: java class interface extends implements


【解决方案1】:

在我深入研究泛型之前...... 为什么不直接引入 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 &amp; 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。但这也意味着您将收到 Unchecked cast from CheckableView to T 警告,因为如果客户端将其转换为 CheckableView 以外的任何其他类型,例如OhterView,你会得到一个ClassCastException。

由于get 方法的实现者无法知道客户端会将其转换为哪种类型,因此他无法确定要返回哪个对象。

如果实现者返回一个特定的对象(例如CheckableView),那么客户端只能将其转换为该类型。那么为什么实现者不更改方法签名以准确返回该类型呢? .... 或者为这种情况引入一个特殊的类型或接口?

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

创建get方法将返回的接口,例如

public interface CheckableView {
}

更改 get 方法以返回 CheckableView

private CheckableView get() {
}

编写一个适配器,使实现Checkable 的Views 适应CheckableView

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

    private T checkableView;

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

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

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

【讨论】:

  • 如果checkableView 不是CheckableView 怎么办?例如:CheckBox.
  • 这是有问题的,因为我认为我不能将 mView 转换为新类。 mView 可以是任何类实例,但我保证它从 View 扩展并实现 Checkable 。另外,关于 View 是一个接口,这是有问题的,因为它是 Android API 的一部分。
  • View 是 android.view.View,创建自己的没有帮助。
  • @Eng.Fouad 我知道 android 中的 View 是一个类而不是一个接口。这就是为什么我说“如果会的话”
  • @RenéLink 我不明白你建议做什么。
【解决方案2】:

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

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

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

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

所以,改成:

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

注意泛型交集,类必须在接口之前。

【讨论】:

  • 阅读他问题的第一部分。
  • 我已经尝试过“&”方式。它没有用。也许我错过了什么?
【解决方案3】:

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

如果您强制调用使用兼容类型View &amp; 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
}

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

【讨论】:

  • 不错。不知道我能做到这一点。如果要返回的接口不止1个怎么办?
  • @androiddeveloper:你的意思是例如View &amp; Checkable &amp; Closeable?只需在每个类型参数定义处通过&amp; 加入所有必需的接口即可。
  • 好的。再次感谢您
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多