【问题标题】:Can you cast an object to an interface it doesn't explicitly implement?您可以将对象转换为未明确实现的接口吗?
【发布时间】:2019-10-15 17:46:27
【问题描述】:

我对实施Strategy pattern 特别感兴趣,其中一个(或多个)策略来自第三方库。

我有一个像这样的类的第三方库:

public class LibraryClass {
    public void doSomething() {
        // ...
    }
}

然后我用相同的接口创建了自己的类:

public class MyClass {
    public void doSomething() {
        // ...
    }
}

现在在我的代码中,我想执行以下操作:

public class MainActivity extends AppCompatActivity {
    public interface Strategy {
        void doSomething();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Strategy oneClass = (Strategy)(new MyClass());
        Strategy twoClass = (Strategy)(new LibraryClass());
        oneClass.doSomething();
        twoClass.doSomething();
    }
}

在这种情况下,该方法存在,因此该接口在技术上有效,但并未明确实现。当我尝试这样做时,它抛出了一个运行时错误:

java.lang.ClassCastException: MyClass cannot be cast to MainActivity$Controls

最坏的情况我知道我可以使用Proxy patternLibraryClass 包装在确实 实现Strategy 接口的东西中:

public class LibraryClassProxy implements Strategy {
    private LibraryClass internal;
    public void LibraryClassProxy() {
        internal = new LibraryClass();
    }
    public void doSomething() {
        internal.doSomething();
    }
}

但如果可能的话,我宁愿避免这种情况 - 特别是因为我要扩展的类实现了许多其他接口,我宁愿不这样做必须代理每个公共方法。

【问题讨论】:

  • 方法定义无关。对象必须实现要转换为它的类/接口。

标签: java android design-patterns interface


【解决方案1】:

正如其他人所指出的,Java 接口定义基于显式声明。我相信您所描述的情况是 Adapter 设计模式涵盖的,而不是代理:

意图:将一个类的接口转换成客户期望的另一个接口。 Adapter 让那些因为接口不兼容而无法协同工作的类。 ---GoF p。 139

您指出的“代理”解决方案对应于适配器模式的 object 变体。如果LibraryClass 不是最终的,可以考虑的一种选择是使用模式的 class 变体:

class MyLibraryClass extends LibraryClass implements Strategy {
   /* ... */
}

【讨论】:

  • 由于令人难以置信的优雅解决方案(扩展 LibraryClass 所以我不必重新实现任何方法),因此给出了公认的答案。当我对其进行测试时,有 一个 小警告:Java 抱怨 LibraryClass 没有默认构造函数(老实说:我不确定如何指定一个“默认“构造函数),所以我必须为MyLibraryClass 实现一个构造函数,如下所示:public MyLibraryClass() { super() }
【解决方案2】:

来自Java Docs

当你定义一个新的接口时,你就是在定义一个新的参考数据 类型。您可以在任何可以使用任何其他数据的地方使用接口名称 键入名称。如果您定义一个类型为 接口,你分配给它的任何对象必须是一个类的实例 实现接口

因此,您不能将对象强制转换为未明确实现的接口

【讨论】:

    【解决方案3】:

    您想要实现的基本上是duck typing 的一种形式:您有一个带有doSomething 方法的接口,并且您希望能够通过该方法传递任何东西作为该接口的实现,即使它没有明确指定它实现了接口。

    你不能在 Java 中做那种鸭式打字; 所有类型的关系必须是明确的。但是通过 lambdas 和 SAM 转换,您可以做一些非常接近的事情。

    public class MainActivity extends AppCompatActivity {
        public interface Strategy {
            void doSomething();
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            Strategy oneClass = new MyClass()::doSomething;
            Strategy twoClass = new LibraryClass()::doSomething;
    
            oneClass.doSomething();
            twoClass.doSomething();
        }
    }
    

    不要尝试强制转换(您不能),而是使用所需对象的 doSomething 方法创建新的 Strategy 对象。就个人而言,这感觉像是一个肮脏的黑客,但是,嘿,这就是你想要的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-31
      • 2013-02-02
      • 1970-01-01
      • 2015-04-11
      • 1970-01-01
      • 2016-08-20
      • 1970-01-01
      相关资源
      最近更新 更多