简体   繁体   中英

Why can't lambda class load?

Why do I get an exception when I run the following code? I went to the source code and found that this class was generated by ASM. Isn't the class generated in this way in the method area?

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<? extends Runnable> runClass = test().getClass();
        ClassLoader classLoader = runClass.getClassLoader();

        System.out.println(classLoader.loadClass(Main.class.getName()));
        System.out.println(classLoader.loadClass(runClass.getName()));
    }

    public static Runnable test() {
        return () -> {};
    }
}

results

class link.yxw.Main
Exception in thread "main" java.lang.ClassNotFoundException: link.yxw.Main$$Lambda$1/1324119927
    at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    at link.yxw.Main.main(Main.java:10)

Short answer is: because the lambda class is hidden ( JEP 371 in JDK15 standardized it and exposed an API, but it's been present since JDK 7) and cannot be accessed by other classes. You can find more information in blogposts and articles like this one .


If we define:

public static Runnable lambdaTest() {
    return () -> {
    };
}

public static Runnable anonymTest() {
    return new Runnable() {
        @Override
        public void run() {
        }
    };
}

and run:

System.out.println(Main.class.isHidden());
System.out.println(anonymTest().getClass().isHidden());
System.out.println(lambdaTest().getClass().isHidden());

we'll get:

false
false
true

If we print the name of the lambda class, we'll get different results on different JDK, but both boiling down to the same concept:

JDK 8-10: java.lang.ClassNotFoundException: Main$$Lambda$1/791452441
JDK 11+:  java.lang.ClassNotFoundException: Main$$Lambda$1/0x0000000800060840

Class#getName() JavaDoc says:

If the class or interface is hidden, then the result is a string of the form: N + '/' + <suffix> where N is the binary name indicated by the class file passed to Lookup::defineHiddenClass, and is an unqualified name.

If you wanted to see the implementation: native JDK code for JDK 9 can be found here ( here's a short description of mirror classes ) and here's the JDK 18 code (been there since 15). If you want to read more, I recommend this SO answer .


Finally let's see the ClassLoader code that looks up the class for loading:

protected final Class<?> findLoadedClass(String name) {
    if (!checkName(name))
        return null;
    return findLoadedClass0(name);
}

// true if the name is null or has the potential to be a valid binary name
private static boolean checkName(String name) {
    if ((name == null) || (name.isEmpty()))
        return true;
    if ((name.indexOf('/') != -1) || (name.charAt(0) == '['))
        return false;
    return true;
}

If the class name contains a / char, it's resolved as not a valid binary name and cannot be loaded. If you wondered about the [ char - it marks an array class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM