Это 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/”