【问题标题】:Java: Creating an anonymus class from a interface passed as an argumentJava:从作为参数传递的接口创建匿名类
【发布时间】:2020-06-10 09:52:50
【问题描述】:

这与 Java 8 接口有关。我有一个只有默认方法的界面。我可以创建这个接口的匿名实例。

public interface Itest{
    default String get(){
            return "My name is ITest";
    }
}

public class A{
     Itest itest = new Itest(){};
}

我想创建一个提供接口的实用程序函数,返回匿名类。类似于以下内容

class MyUtil{
    public static <T> T get(Class<T> interface){
        // returns the anonymous class for the interface argument
        // Something like return new interface(){};
    }
}

public class A{
     Itest itest = MyUtil.get(Itest.class);
}

如果可能的话,有人帮助我以及如何在 MyUtil.get() 函数中实现它?

用例:我很少有这样的接口(使用默认方法)。现在,如果在设置中这些接口没有可用的实现类,我想创建匿名类实例以便可以调用默认方法。当我只需要这些默认方法本身时,这将节省创建实现类的工作。

【问题讨论】:

  • 您究竟为什么要这样做?你的最终目标是什么?为什么MyUtil.get(...) 会帮助您实现目标?因为你不能像你想做的那样从Class&lt;?&gt; 实例化一个接口,即使你的接口只包含默认方法。您可能会通过使用 Proxy 而侥幸逃脱,但它很难
  • 感谢@OlivierGrégoire 的建议。关于用例:我将有许多带有默认方法的接口。对于每个接口,可以有多个基于平台的实现。但大多会使用默认方法。因此希望尽可能避免创建空的 impl 类。因此,希望创建一个 Util 方法,如果实现类可用,该方法可以创建一个 newInstance() 或返回一个匿名类。这个 Util 方法将帮助我在一个地方处理这些接口。希望这能回答问题。

标签: java generics interface anonymous-class


【解决方案1】:

这是一个非常有趣的问题。我想到的第一个解决方案是一个简单的实例化:

public static <T> T get(Class<T> clazz) throws ... {
    return clazz.getDeclaredConstructor().newInstance();
}

但结果是:

java.lang.NoSuchMethodException: my.package.Itest.()

记住接口没有构造函数,不能直接实例化。

第 1 步:创建动态代理类

你必须使用java.lang.reflect.Proxy 来创建一个动态代理类来代表一个匿名类实现接口。

public static <T> T get(Class<T> clazz) {
    return (T) java.lang.reflect.Proxy.newProxyInstance(
        clazz.getClassLoader(),
        new java.lang.Class[] { clazz },
        (proxy, method, args) -> null
    );
}

用法:

Itest itest = MyUtil.get(Itest.class);
System.out.println(itest.getClass().getName());  // com.sun.proxy.$Proxy2
System.out.println(itest instanceof Shit.Itest); // true

// all methods invokation are completely ignored (returning null)
System.out.println(itest.get());                 // null 

InvocationHandler::invoke 的 lambda 表达式总是返回 null,只要它是一个“空包装器”,无论调用什么带有 whaat 参数的方法。

第 2 步:调用默认方法

只要你想调用一个默认方法,你可以使用修改过的简单技巧在How do I invoke Java 8 default methods reflectively回答:

public static <T> T get(Class<T> clazz) {
    return (T) java.lang.reflect.Proxy.newProxyInstance(
        clazz.getClassLoader(),
        new java.lang.Class[]{clazz},
        (proxy, method, args) -> {
            if (method.isDefault()) {
                return MethodHandles.lookup()
                    .in(method.getDeclaringClass())
                    .unreflectSpecial(method,method.getDeclaringClass())
                    .bindTo(proxy)
                    .invokeWithArguments(args);
            }
            return null;
        }
    );
}

我们开始吧:

Itest itest = MyUtil.get(Itest.class);
System.out.println(itest.getClass().getName());  // com.sun.proxy.$Proxy2
System.out.println(itest instanceof Shit.Itest); // true

// default methods are invoked, the implementation is taken from the interface
System.out.println(itest.get());                 // My name is ITest

【讨论】:

  • 感谢 sn-p。它适用于 Java 8,并基于 link final Constructor&lt;Lookup&gt; constructor = Lookup.class.getDeclaredConstructor(Class.class); constructor.setAccessible(true) ; 进行了一些额外的更改
  • 那么,您的问题解决了吗?我很高兴我至少为您指明了正确的方向。
【解决方案2】:

我认为不可能使用Class&lt;?&gt; 创建接口的实例。您是否尝试过绕过 MyUtil 类并将所有接口方法设为静态。这将使您能够调用这些方法而无需启动它们。例如:

public interface Itest {
    static String get() {
        return "My name is ITest";
    }
}


public class TryingItest {

    public static void main(String[] args) {
        System.out.println(Itest.get());
    }

}

这会将需要实现的接口部分和静态部分分开。希望这会有所帮助。

【讨论】:

  • 感谢您的解决方案。但后来我有一个不同的用例。基于平台的接口可以有多个 impl 类。大多数 impl 类将使用可能不是静态的默认方法。因此,如果我可以避免创建这些空的 impl 类并且只实现需要的类,那么正在尝试。然后 MyUtil.get() 可以提供 impl 类(如果存在)或创建匿名类。我也将有多个这样的接口。因此希望创建一个 Util 方法来解析所有接口。
【解决方案3】:
interface Itest{
    default String get(){
            return "My name is ITest";
    }
}

class ITestImpl implements Itest {

}

class MyUtil{
    public static <T extends Itest> T get(Class<T> impl) {
        try {
            return impl.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        }
    }
}

然后

Itest test = MyUtil.get(ITestImpl.class);

【讨论】:

  • 测试过吗?如果impl 代表interface,我很确定这将失败
  • 基于平台可以有多个impl类。大多数 impl 类将使用默认方法。因此,如果我可以避免创建这些空的 impl 类而只实现需要的类,那么我正在尝试。MyUtil.get() 然后可以提供 impl 类或创建匿名类。
猜你喜欢
  • 2011-02-21
  • 2011-10-01
  • 2016-07-22
  • 1970-01-01
  • 1970-01-01
  • 2012-05-24
  • 1970-01-01
  • 2019-11-23
相关资源
最近更新 更多