加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – 如何创建一个我无法更改的类,实现一个接口?

发布时间:2020-12-14 23:46:51 所属栏目:Java 来源:网络整理
导读:我有一个来自另一个闭源的库的类,但我希望能够使用它的接口.原因是我不想在任何地方进行instanceof检查或空检查,但我也不想扩展现有的类. 例如,假设我有这样的代码: public class Example { // QuietFoo is from another library that I can't change priva
我有一个来自另一个闭源的库的类,但我希望能够使用它的接口.原因是我不想在任何地方进行instanceof检查或空检查,但我也不想扩展现有的类.

例如,假设我有这样的代码:

public class Example {

    // QuietFoo is from another library that I can't change
    private static QuietFoo quietFoo;
    // LoudFoo is my own code and is meant to replace QuietFoo
    private static LoudFoo loudFoo;

    public static void main(String[] args) {
        handle(foo);
    }

    private static void handle(Object foo) {
        if (foo instanceof QuietFoo)
            ((QuietFoo) foo).bar();
        else if (foo instanceof LoudFoo)
            ((LoudFoo) foo).bar();
    }
}

我不能改变QuietFoo:

public class QuietFoo {

    public void bar() {
        System.out.println("bar");
    }
}

但我可以改变LoudFoo:

public class LoudFoo {

    public void bar() {
        System.out.println("BAR!!");
    }
}

问题是,在许多类中可能还有许多其他的bar实现,并且可能有更多的方法而不仅仅是bar,所以不仅我的handle方法会因为大量的instanceof语句而变得缓慢而丑陋,但是我不得不写一个这些处理QuietFoo和LoudFoo上每个方法的方法.扩展不是一个可行的解决方案,因为它违反了整个合同,因为LoudFoo不是QuietFoo.

基本上,给予Foo:

public interface Foo {
    void bar();
}

如何在不更改源的情况下使QuietFoo实现Foo,这样我就不必在代码中的任何地方进行转换和instanceof调用?

解决方法

有两种方法:

>使用适配器模式
>使用Proxy

适配器方法将更简单但灵活性更低,并且代理方法将更复杂但更灵活.尽管代理方法更复杂,但这种复杂性仅限于几个类.

适配器

adapter pattern很简单.对于您的示例,它只是一个类,如下所示:

public class QuietFooAdapter implements Foo {

    private QuietFoo quietFoo;

    public QuietFooAdapter(QuietFoo quietFoo) {
        this.quietFoo = quietFoo;
    }

    public void bar() {
        quietFoo.bar();
    }
}

然后使用它:

Foo foo = new QuietFooAdapter(new QuietFoo());
foo.bar();

这很好,但如果你有一个以上的类来制作适配器,这可能是单调乏味的,因为你需要为每个必须包装的类添加一个新的适配器.

Java的代理类

Proxy是一个本机Java类,它是反射库的一部分,允许您创建更通用的反射解决方案.它涉及3个部分:

>界面(在这种情况下,Foo)
> InvocationHandler
>创建代理(Proxy.newProxyInstance)

我们已经有了界面,所以我们在那里很好.

InvocationHandler是我们通过反射“自动适应”的地方:

public class AdapterInvocationHandler implements InvocationHandler {

    private Object target;
    private Class<?> targetClass;

    public AdapterInvocationHandler(Object target) {
        this.target = target;
        targetClass = target.getClass();
    }

    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
        try {
            Method targetMethod = targetClass.getMethod(method.getName(),method.getParameterTypes());
            if (!method.getReturnType().isAssignableFrom(targetMethod.getReturnType()))
                throw new UnsupportedOperationException("Target (" + target.getClass().getName() + ") does not support: " + method.toGenericString());
            return targetMethod.invoke(target,args);
        } catch (NoSuchMethodException ex) {
            throw new UnsupportedOperationException("Target (" + target.getClass().getName() + ") does not support: " + method.toGenericString());
        } catch (IllegalAccessException ex) {
            throw new UnsupportedOperationException("Target (" + target.getClass().getName() + ") does not declare method to be public: " + method.toGenericString());
        } catch (InvocationTargetException ex) {
            // May throw a NullPointerException if there is no target exception
            throw ex.getTargetException();
        }
    }
}

这里重要的代码在try块中.这将处理将代理上调用的任何方法调用适配到内部目标对象的过程.如果在不支持的接口上调用了一个方法(非公共,错误的返回类型,或者只是不存在),那么我们抛出UnsupportedOperationException.如果我们捕获InvocationTargetException,我们将通过InvocationTargetException.getTargetException重新抛出导致它的异常.当我们调用的方法反射抛出异常时会发生这种情况. Java将其包装在一个新的异常中并抛出该新异常.

接下来,我们需要一些东西来创建适配器:

public class AdapterFactory {

    public static <T> T createAdapter(Object target,Class<T> interfaceClass) {
        if (!interfaceClass.isInterface())
            throw new IllegalArgumentException("Must be an interface: " + interfaceClass.getName());
        return (T) Proxy.newProxyInstance(null,new Class<?>[] { interfaceClass },new AdapterInvocationHandler(target));
    }
}

如果愿意,您还可以将AdapterInvocationHandler类嵌套在AdapterFactory类中,以便AdapterFactory中的所有内容都是自包含的.

然后使用它:

Foo foo = AdapterFactory.createAdapter(new QuietFoo(),Foo.class);
foo.bar();

这种方法比实现单个适配器需要更多的代码,但是它足够通用,可以用于为任何类和接口对创建自动适配器,而不仅仅是QuietFoo和Foo示例.当然,这种方法使用反射(Proxy类使用反射,我们的InvocationHandler也是如此),这可能会更慢,但JVM的最近改进使得反射比以前快得多.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读