请考虑以下情形:
假设您创建了一个界面Foo
:
public interface Foo {
public void bar();
}
并说您要使用的某个库中有一个旧类SomeOldClass
。它已经具有bar()
方法,但是没有显式实现Foo
。
您已经为实现Foo
的所有分类编写了以下代码:
public <T extends Foo> T callBarOnThird(List<T> fooList){
return fooList.get(2).bar();
}
现在您希望它也适用于SomeOldClass
。您无权访问此类的源代码,因此无法对其进行修改。
有没有一种方法来声明Foo
或类似某种“软”接口的方法(例如,在其中实现所有必需方法的任何类都将被接受为该软接口的隐式实现) ?如果没有,您将如何用尽可能干净的代码解决这个问题?
答案 0 :(得分:5)
不,不是。
您必须提供一个适配器实例(有多种方法和工具可以帮助您实现,但是Java并非“隐式”做到这一点。)
答案 1 :(得分:4)
Java是静态类型和动态绑定。
动态绑定::这意味着方法签名及其实现之间的链接在运行时发生。例如。
例如
public interface MyInterface {
void doStuff();
}
public class MyFirstImpl implements MyInterface {
@Override
public void doStuff() {
// do some stuff here
}
}
public class MySecondImpl implements MyInterface {
@Override
public void doStuff() {
// do some stuff here
}
}
因此,如果您有下一个摘要
MyInterface test; // pointing to either MyFirstImpl or MySecondImpl
test.doStuff();
JVM将根据对象的运行时类型,在运行时确定要从doStuff
或MyFirstImpl
调用MySecondImpl
方法。
静态类型::这意味着JVM将在编译时检查是否有调用方法,而与实现无关。
例如:
public interface MyInterface {
void doStuff();
}
public class MyFirstImpl implements MyInterface {
// no override here
public void doStuff() {
// do some stuff here
}
}
public class MySecondImpl implements MyInterface {
// no override here
public void doStuff() {
// do some stuff here
}
}
因此,如果您有下一个摘要
MyInterface test; // pointing to either MyFirstImpl or MySecondImpl
test.doStuff();
编译器会抱怨,因为它无法确保在编译时不管MyInterface
的实现如何都调用doStuff
方法(尽管在这种情况下,{{1 }}定义一个MyInterface
方法。
这可以确保您不会在运行时得到doStuff
,例如,如果您通过了下一个实现。
NoSuchMethodException
这为语言增加了某种类型的安全性,但又有一定的刚性(因为您能够比运行时更早地确定问题,因此反馈循环更短,但以所有实现的情况为代价)实际上公开了无法使用的方法。
如何重构代码:
在第三方库上创建包装器,并从包装器公开接口。
public class MySecondImpl implements MyInterface {
// no override here
// no doStuff method
}
然后,在您的代码中使用public interface Foo {
void bar();
}
public class ThirdPartyFooWrapper implements Foo {
private SomeOldClass oldClass;
public ThordPartyFooWrapper (SomeOldClass oldClass){
this.oldClass = oldClass;
}
@Override
public void bar() {
this.oldClass.bar();
}
}
而不是ThirdPartyFooWrapper
。
希望这能回答您的问题!
答案 2 :(得分:0)
扩展到Thilos答案。
您也可以使用装饰器来处理
public <T extends Foo> T callBarOnThird(List<T> fooList){
return new BarDecorator(fooList.get(2)).bar();
}
在装饰器内部,您可以检查给定的Object是否为Foo的实例,然后进行相应的操作。