java8でオブジェクトを呼び出す親メソッドを実装します

public class GrandFather {
    
    

    protected void thinking() {
    
    
        System.out.println("I 'm grandFather!");
    }
}
public class Father extends GrandFather {
    
    

    @Override
    protected void thinking() {
    
    
        System.out.println("I 'm father!");
    }

    private void hello() {
    
    
        System.out.println("father say hello !!!");
    }
}
public class Son extends Father {
    
    

    @Override
    protected void thinking() {
    
    
        System.out.println("I 'm Son!");
    }
}

jdk8は直接呼び出された場合はMethodHandles.lookup()取得MethodHandles.Lookup
メソッドを呼び出すことにMethodHandles.Lookup.findSpecial(Class, String, invoke.MethodType, Class)
し、MethodHandles.Lookup.unreflectSpecial(Method, Class)親クラスのメソッドへのハンドルを取得MethodHandleする際
の可能なアクセスが十分ではありませんので、問題を解決するためMethodHandles.Lookupを作成して反射することにより、以下の例外を投げます。

     java.lang.IllegalAccessException: no private access for invokespecial: interface com.example.demo.methodhandle.UserService, from com.example.demo.methodhandle.UserServiceInvoke
   at java.lang.invoke.MemberName.makeAccessException(MemberName.java:850)
   at java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:1572)

直接MethodHandles.lookup()取得されたjdk11呼び出しMethodHandles.Lookupアクセス許可のみを取得するためのインターフェイスメソッドタイプのみメソッドハンドルMethodHandleです。これが一般的なタイプのクラスである場合は、MethodHandles#privateLookupIn(java.lang.Class, java.lang.invoke.MethodHandles.Lookup)メソッドの提供を開始したjdk9を使用する必要があります

特定の理由については、以下を参照してください。java.lang.invoke.MethodHandles.Lookup#checkSpecialCaller
java8:

private void checkSpecialCaller(Class<?> specialCaller) throws IllegalAccessException {
    
    
            int allowedModes = this.allowedModes;
            if (allowedModes == TRUSTED)  return;
            if (!hasPrivateAccess()
                || (specialCaller != lookupClass()
                    && !(ALLOW_NESTMATE_ACCESS &&
                         VerifyAccess.isSamePackageMember(specialCaller, lookupClass()))))
                throw new MemberName(specialCaller).
                    makeAccessException("no private access for invokespecial", this);
        }

java11:

private void checkSpecialCaller(Class<?> specialCaller, Class<?> refc) throws IllegalAccessException {
    
    
    int allowedModes = this.allowedModes;
    if (allowedModes == TRUSTED)  return;
    if (!hasPrivateAccess()
        || (specialCaller != lookupClass()
               // ensure non-abstract methods in superinterfaces can be special-invoked
            && !(refc != null && refc.isInterface() && refc.isAssignableFrom(specialCaller))))
        throw new MemberName(specialCaller).
            makeAccessException("no private access for invokespecial", this);
}

解決策:次のツールを参照してください

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * jdk8中如果直接调用{@link MethodHandles#lookup()}获取到的{@link MethodHandles.Lookup}
 * <p>
 * 在调用方法 {@link MethodHandles.Lookup#findSpecial(java.lang.Class, java.lang.String, java.lang.invoke.MethodType, java.lang.Class)}
 * <p>
 * 和{@link MethodHandles.Lookup#unreflectSpecial(java.lang.reflect.Method, java.lang.Class)}
 * 获取父类方法句柄{@link MethodHandle}时
 * <p>
 * 可能出现权限不够, 抛出如下异常, 所以通过反射创建{@link MethodHandles.Lookup}解决该问题.
 * <pre>
 *   java.lang.IllegalAccessException: no private access for invokespecial: interface com.example.demo.methodhandle.UserService, from com.example.demo.methodhandle.UserServiceInvoke
 * at java.lang.invoke.MemberName.makeAccessException(MemberName.java:850)
 * at java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:1572)
 * </pre>
 * <p>
 * 而jdk11中直接调用{@link MethodHandles#lookup()}获取到的{@link MethodHandles.Lookup},也只能对接口类型才会权限获取方法的方法句柄{@link MethodHandle}.
 * <p>
 * 如果是普通类型Class,需要使用jdk9开始提供的 MethodHandles#privateLookupIn(java.lang.Class, java.lang.invoke.MethodHandles.Lookup)方法.
 */
public class MethodHandlesUtil {
    
    
    private static final Logger logger = LoggerFactory.getLogger(MethodHandlesUtil.class);

    private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
            | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;

    private static Constructor<MethodHandles.Lookup> java8LookupConstructor;
    private static Method privateLookupInMethod;

    static {
    
    
        //先查询jdk9 开始提供的java.lang.invoke.MethodHandles.privateLookupIn方法,
        //如果没有说明是jdk8的版本.(不考虑jdk8以下版本)
        try {
    
    
            privateLookupInMethod = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
        } catch (NoSuchMethodException e) {
    
    
            privateLookupInMethod = null;
            logger.info("There is no [java.lang.invoke.MethodHandles.privateLookupIn(Class, Lookup)] method in this version of JDK");
        }
        //jdk8
        //这种方式其实也适用于jdk9及以上的版本,但是上面优先,可以避免 jdk9 反射警告
        if (privateLookupInMethod == null) {
    
    
            try {
    
    
                java8LookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
                java8LookupConstructor.setAccessible(true);
            } catch (NoSuchMethodException e) {
    
    
                //可能是jdk8 以下版本
                throw new IllegalStateException(
                        "There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",
                        e);
            }
        }
    }

    /**
     * java9中的MethodHandles.lookup()方法返回的Lookup对象
     * 有权限访问specialCaller != lookupClass()的类
     * 但是只能适用于接口, {@link java.lang.invoke.MethodHandles.Lookup#checkSpecialCaller}
     */
    public static MethodHandles.Lookup lookup(Class<?> callerClass) {
    
    
        //使用反射,因为当前jdk可能不是java9或以上版本
        if (privateLookupInMethod != null) {
    
    
            try {
    
    
                return (MethodHandles.Lookup) privateLookupInMethod.invoke(MethodHandles.class, callerClass, MethodHandles.lookup());
            } catch (IllegalAccessException | InvocationTargetException e) {
    
    
                throw new RuntimeException(e);
            }
        }
        //jdk 8
        try {
    
    
            return java8LookupConstructor.newInstance(callerClass, ALLOWED_MODES);
        } catch (Exception e) {
    
    
            throw new IllegalStateException("no 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.", e);
        }
    }

    public static MethodHandle getSpecialMethodHandle(Method parentMethod) {
    
    
        final Class<?> declaringClass = parentMethod.getDeclaringClass();
        MethodHandles.Lookup lookup = lookup(declaringClass);
        try {
    
    
            return lookup.unreflectSpecial(parentMethod, declaringClass);
        } catch (IllegalAccessException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}
public class MethodHandleTest {
    
    

    public static void main(String[] args) throws Throwable {
    
    
        Son son = new Son();
        son.thinking();
        
        MethodHandles.Lookup lookup = MethodHandlesUtil.lookup(Father.class);
        MethodType mt = MethodType.methodType(void.class);
        MethodHandle thinking = lookup.findSpecial(Father.class, "thinking", mt, Father.class);
        thinking.invoke(son);

        MethodHandles.Lookup lookup2 = MethodHandlesUtil.lookup(GrandFather.class);
        MethodHandle thinking2 = lookup2.findSpecial(GrandFather.class, "thinking", mt, GrandFather.class);
        thinking2.invoke(son);

        MethodHandle specialMethodHandle = MethodHandlesUtil.getSpecialMethodHandle(
                GrandFather.class.getDeclaredMethod("thinking"));
        specialMethodHandle.invoke(son);

        //反射调用
        Method hello1 = Father.class.getDeclaredMethod("hello");
        hello1.setAccessible(true);
        hello1.invoke(son);

        //调用父类中的私有方法,因为子类没法重写父类的私有方法,所以调用结果跟反射一样.
        MethodHandles.Lookup lookup1 = MethodHandlesUtil.lookup(Father.class);
        MethodHandle methodHandle = lookup1.unreflectSpecial(hello1, Father.class);
        methodHandle.invoke(son);

    }
}

結果:

ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/u013202238/article/details/108687086