Это JEP 181: Управление доступом на основе Nest , поддерживает частный доступ внутри участников nest напрямую, не более чем с помощью автоматически сгенерированного метода моста доступ $000
. Кроме того, новые API-интерфейсы nest для проверки и разрешили доступ к частному отражению внутри участников nest.
P.S Нет необходимости изменять код, это оптимизация компилятора Java для удаления доступа к методу моста.
1. До Java 11 доступ к мостовому методу составлял $000
1.1 Просмотрите следующий вложенный класс. Все вложенные классы пытаются получить доступ к закрытому члену Алфавита
, он же Узел гнезда
.
public class Alphabet { private String name = "I'm Alphabet!"; public class A { public void printName() { System.out.println(name); // access Alphabet's private member! } } public class B { public void printName() { System.out.println(name); // access Alphabet's private member! } public class B1 { public void printName() { System.out.println(name); // access Alphabet's private member! } } } }
1.2 Если мы скомпилируем приведенный выше источник, он сгенерирует четыре класса, Алфавит
, Алфавит$A
, Алфавит$B
и Алфавит$B.B1
, даже вложенный класс является типичным классом с уникальным именем. Правило доступа JVM не разрешает частный доступ в разных классах.
Однако Java разрешила частный доступ внутри членов nest, поэтому компилятор Java создает метод моста access$000
для применения к правилу доступа JVM.
$ javac Alphabet.java Alphabet$A.class Alphabet$B.class Alphabet$B.B1.class Alphabet.class
1.3 Исходный код доступа к мостовому методу аналогичен этому:
public class Alphabet { private String name = "I'm Alphabet!"; String access$000(){ return name; } } public class Alphabet$A { final Alphabet obj; public void printName(){ System.out.println(obj.access$000()); } } public class Alphabet$B { final Alphabet obj; public void printName(){ System.out.println(obj.access$000()); } } public class Alphabet$B$B1 { final Alphabet$B this$1; public void printName(){ System.out.println(obj.access$000()); } }
2. Разберите код
Мы можем использовать javap -c
для разборки вышеупомянутых четырех классов, чтобы увидеть разницу до и после доступа к мостовому методу.
2.1 До Java 11 он автоматически создает новый метод access$000
bridge для вложенного частного доступа.
$ javac Alphabet.java $ javap -c Alphabet Compiled from "Alphabet.java" public class Alphabet { public Alphabet(); Code: 0: aload_0 1: invokespecial #2 // Method java/lang/Object."":()V 4: aload_0 5: ldc #3 // String I'm Alphabet! 7: putfield #1 // Field name:Ljava/lang/String; 10: return static java.lang.String access$000(Alphabet); Code: 0: aload_0 1: getfield #1 // Field name:Ljava/lang/String; 4: areturn } $ javap -c Alphabet$A Compiled from "Alphabet.java" public class Alphabet$A { final Alphabet this$0; public Alphabet$A(Alphabet); Code: 0: aload_0 1: aload_1 2: putfield #1 // Field this$0:LAlphabet; 5: aload_0 6: invokespecial #2 // Method java/lang/Object." ":()V 9: return public void printName(); Code: 0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_0 4: getfield #1 // Field this$0:LAlphabet; 7: invokestatic #4 // Method Alphabet.access$000:(LAlphabet;)Ljava/lang/String; 10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 13: return } $ javap -c Alphabet$B Compiled from "Alphabet.java" public class Alphabet$B { final Alphabet this$0; public Alphabet$B(Alphabet); Code: 0: aload_0 1: aload_1 2: putfield #1 // Field this$0:LAlphabet; 5: aload_0 6: invokespecial #2 // Method java/lang/Object." ":()V 9: return public void printName(); Code: 0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_0 4: getfield #1 // Field this$0:LAlphabet; 7: invokestatic #4 // Method Alphabet.access$000:(LAlphabet;)Ljava/lang/String; 10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 13: return } $ javap -c Alphabet$B$B1 Compiled from "Alphabet.java" public class Alphabet$B$B1 { final Alphabet$B this$1; public Alphabet$B$B1(Alphabet$B); Code: 0: aload_0 1: aload_1 2: putfield #1 // Field this$1:LAlphabet$B; 5: aload_0 6: invokespecial #2 // Method java/lang/Object." ":()V 9: return public void printName(); Code: 0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_0 4: getfield #1 // Field this$1:LAlphabet$B; 7: getfield #4 // Field Alphabet$B.this$0:LAlphabet; 10: invokestatic #5 // Method Alphabet.access$000:(LAlphabet;)Ljava/lang/String; 13: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 16: return }
2.2 В Java 11 он поддерживает частный доступ внутри членов nest напрямую, без метода моста.
Протестировано с Java 11.
$ javac Alphabet.java $ javap -c Alphabet Compiled from "Alphabet.java" public class Alphabet { public Alphabet(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: aload_0 5: ldc #2 // String I\'m Alphabet! 7: putfield #3 // Field name:Ljava/lang/String; 10: return } $ javap -c Alphabet$A Compiled from "Alphabet.java" public class Alphabet$A { final Alphabet this$0; public Alphabet$A(Alphabet); Code: 0: aload_0 1: aload_1 2: putfield #1 // Field this$0:LAlphabet; 5: aload_0 6: invokespecial #2 // Method java/lang/Object." ":()V 9: return public void printName(); Code: 0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_0 4: getfield #1 // Field this$0:LAlphabet; 7: getfield #4 // Field Alphabet.name:Ljava/lang/String; 10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 13: return } $ javap -c Alphabet$B Compiled from "Alphabet.java" public class Alphabet$B { final Alphabet this$0; public Alphabet$B(Alphabet); Code: 0: aload_0 1: aload_1 2: putfield #1 // Field this$0:LAlphabet; 5: aload_0 6: invokespecial #2 // Method java/lang/Object." ":()V 9: return public void printName(); Code: 0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_0 4: getfield #1 // Field this$0:LAlphabet; 7: getfield #4 // Field Alphabet.name:Ljava/lang/String; 10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 13: return } $ javap -c Alphabet$B$B1 Compiled from "Alphabet.java" public class Alphabet$B$B1 { final Alphabet$B this$1; public Alphabet$B$B1(Alphabet$B); Code: 0: aload_0 1: aload_1 2: putfield #1 // Field this$1:LAlphabet$B; 5: aload_0 6: invokespecial #2 // Method java/lang/Object." ":()V 9: return public void printName(); Code: 0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_0 4: getfield #1 // Field this$1:LAlphabet$B; 7: getfield #4 // Field Alphabet$B.this$0:LAlphabet; 10: getfield #5 // Field Alphabet.name:Ljava/lang/String; 13: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 16: return }
3. Новые API-интерфейсы классов вложенности.
3.1 Некоторые новые API-интерфейсы для проверки вложенных элементов.
getnexthost - получить
получить участников Гнезда
является ли Гнездо партнером
Например, просмотрите следующие вложенные классы. Алфавит
является Хостом Гнезда
, а все остальные являются Членами Гнезда
или Оценка
друг другу.
public class Alphabet { public class A { } public class B { public class B1 { } } }
3.2 Приведенный ниже исходный код показывает использование новых вложенных API. Единственный трюк – это B.B1.class.getmethod()
возвращает Алфавит
, а не B
. Другие результаты очевидны, читайте комментарии.
package com.mkyong.java11.jep181; import java.util.Arrays; public class Alphabet { private String name = "I'm Alphabet!"; public class A { public void printName() { System.out.println(name); // access Alphabet's private member! } } public class B { public void printName() { System.out.println(name); // access Alphabet's private member! } public class B1 { public void printName() { System.out.println(name); // access Alphabet's private member! } } } public static void main(String[] args) { A objA = new Alphabet().new A(); objA.printName(); B objB = new Alphabet().new B(); objB.printName(); B.B1 objB1 = new Alphabet().new B().new B1(); objB1.printName(); System.out.println(Alphabet.class.getNestHost()); // Alphabet System.out.println(A.class.getNestHost()); // Alphabet System.out.println(B.class.getNestHost()); // Alphabet System.out.println(B.B1.class.getNestHost()); // Alphabet!, not B System.out.println("---"); System.out.println(Arrays.toString(Alphabet.class.getNestMembers())); // Alphabet, Alphabet$A, Alphabet$B, Alphabet$B$B1 System.out.println(Arrays.toString(A.class.getNestMembers())); // Alphabet, Alphabet$A, Alphabet$B, Alphabet$B$B1 System.out.println(Arrays.toString(B.class.getNestMembers())); // Alphabet, Alphabet$A, Alphabet$B, Alphabet$B$B1 System.out.println(Arrays.toString(B.B1.class.getNestMembers())); // Alphabet, Alphabet$A, Alphabet$B, Alphabet$B$B1 System.out.println("---"); System.out.println(Alphabet.class.isNestmateOf(Alphabet.class)); // true System.out.println(Alphabet.class.isNestmateOf(A.class)); // true System.out.println(Alphabet.class.isNestmateOf(B.class)); // true System.out.println(Alphabet.class.isNestmateOf(B.B1.class)); // true System.out.println("---"); System.out.println(A.class.isNestmateOf(Alphabet.class)); // true System.out.println(A.class.isNestmateOf(A.class)); // true System.out.println(A.class.isNestmateOf(B.class)); // true System.out.println(A.class.isNestmateOf(B.B1.class)); // true System.out.println("---"); System.out.println(B.class.isNestmateOf(Alphabet.class)); // true System.out.println(B.class.isNestmateOf(A.class)); // true System.out.println(B.class.isNestmateOf(B.class)); // true System.out.println(B.class.isNestmateOf(B.B1.class)); // true System.out.println("---"); System.out.println(B.B1.class.isNestmateOf(Alphabet.class)); // true System.out.println(B.B1.class.isNestmateOf(A.class)); // true System.out.println(B.B1.class.isNestmateOf(B.class)); // true System.out.println(B.B1.class.isNestmateOf(B.B1.class)); // true } }
4. Доступ к отражению
3.1/|Внутренний пытается получить доступ к
Внутреннему закрытому методу через API отражения.
package com.mkyong.java11.jep181; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Outer { public static class InnerA { // InnerA access InnerB private method, via reflection! // Before Java 11 - IllegalAccessException // Java 11 - OK public void printName() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { InnerB obj = new InnerB(); final Method m = InnerB.class.getDeclaredMethod("printName"); m.invoke(obj); } } public static class InnerB { // private!!! private void printName() { System.out.println("I'm InnerB!"); } } public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { InnerA obj = new InnerA(); obj.printName(); } }
До Java 11 он выбрасывает java.lang. Исключение незаконного доступа
.
Exception in thread "main" java.lang.IllegalAccessException: Class com.mkyong.java11.jep181.Outer$InnerA can not access a member of class com.mkyong.java11.jep181.Outer$InnerB with modifiers "private" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102) at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296) at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288) at java.lang.reflect.Method.invoke(Method.java:491) at com.mkyong.java11.jep181.Outer$InnerA.printName(Outer.java:14) at com.mkyong.java11.jep181.Outer.main(Outer.java:28)
Запускайте с Java 11, без проблем. Новый контроль доступа на основе гнезда обеспечивает частный доступ к вложенным членам, даже через API отражения.
I'm InnerB!
Скачать Исходный Код
$клон git https://github.com/mkyong/core-java $cd java-11 $cd src/главная/java/com/mkyong/java 11/jep181
Рекомендации
- JEP 181: Управление доступом на Основе Гнезд
- Проект OpenJDK 11
- Вложенные классы Oracle
- Дженков – Вложенные классы Java
Оригинал: “https://mkyong.com/java/java-11-nest-based-access-control/”