2014-03-24 31 views
9

鉴于这个简单的“Hello World”是Java 8界面,我如何通过反射调用其hello()方法?如何反射地调用Java 8默认方法

public interface Hello { 
    default String hello() { 
     return "Hello"; 
    } 
} 
+0

相关:http://stackoverflow.com/questions/37812393/how-to-explicitly-invoke-default-method-from-a-dynamic-proxy如注释为dup。通过@Rudziankoŭ – earcam

回答

5

你不能直接调用它,因为你需要一个实现类的实例。为此,你需要一个实施课程。 default方法不是static方法,也不能创建接口的实例。

因此,假设你已经实现类:

class HelloImpl implements Hello { } 

您可以调用这样的方法:

Class<HelloImpl> clazz = HelloImpl.class; 
Method method = clazz.getMethod("hello"); 
System.out.println(method.invoke(new HelloImpl())); // Prints "Hello" 
+0

我应该更精确的在我的问题:我如何*实例化它*并调用ist hello()方法 - *都通过反射? – thekid

+2

@thekid你不能实例化一个接口。你需要一个实施课程,我已经在答案中展示了。 –

3

我已经found a solution从像上面的接口创建实例使用反思代码从sun.misc.ProxyGenerator,它通过组装字节码来定义类HelloImpl。现在我可以写:

Class<?> clazz = Class.forName("Hello"); 
Object instance; 

if (clazz.isInterface()) { 
    instance = new InterfaceInstance(clazz).defineClass().newInstance(); 
} else { 
    instance = clazz.newInstance(); 
} 

return clazz.getMethod("hello").invoke(instance); 

......但这很丑陋。

+0

必须是丑陋的,否则有人会想要使用它! –

+0

肯定会比必要的工作更多。访问可以通过MethodHandles完成,不需要大量的依赖或过多的运行时间工作。 – Ajax

15

您可以使用该MethodHandles

import java.lang.invoke.MethodHandles; 
import java.lang.reflect.Method; 
import java.lang.reflect.Proxy; 

public class ReflectiveDefaultMethodCallExample { 

    static interface Hello { 
     default String hello() { 
      return "Hello"; 
     } 
    } 

    public static void main(String[] args) throws Throwable{ 

     Hello target = 
       //new Hello(){}; 
       (Hello)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[]{Hello.class}, (Object proxy, Method method, Object[] arguments) -> null); 
     Method method = Hello.class.getMethod("hello"); 

     Object result = MethodHandles.lookup() 
            .in(method.getDeclaringClass()) 
            .unreflectSpecial(method,method.getDeclaringClass()) 
            .bindTo(target) 
            .invokeWithArguments(); 
     System.out.println(result); //Hello 
    } 
} 
+1

好主意,因为它提供了一个提示'InvocationHandler'如何委托给一个默认方法。这在其他情况下可能非常有用。 – Holger

+4

这是正确的,除了它会在用于查找句柄的文件之外的文件中定义的任何接口失败。我使用了以下修改:final Field field = Lookup.class.getDeclaredField(“IMPL_LOOKUP”); field.setAccessible(true); final Lookup lookup =(Lookup)field.get(null); (); final Object value = lookup .unreflectSpecial(method,method.getDeclaringClass()) .bindTo(t) .invokeWithArguments(); – Ajax

+2

这篇文章的评论也有其他的解决方法https://rmannibucau.wordpress.com/2014/03/27/java-8-default-interface-methods-and-jdk-dynamic-proxies/最好的可能是抢Lookup类的构造函数,因此您可以使用PRIVATE访问创建查找,而不像上面那样访问完全权限的IMPL_LOOKUP字段。 – Ajax