Java 为什么lambda和anonymous类有不同的隐式添加字段?
众所周知,在某些情况下,匿名内部类和捕获lambda都隐式地添加了私有字段。例如,实例字段匿名类,它是对外部类实例的引用,而对于本地和静态匿名实例,则没有任何引用。但本地实例lambda不同:它有一个捕获变量类型的字段。如果最终它们的使用方式相同,为什么会有这种差异?为什么不能在本地匿名类的精确副本中构建本地lambda UPD: 一点样品 类来测试匿名类的字段Java 为什么lambda和anonymous类有不同的隐式添加字段?,java,lambda,Java,Lambda,众所周知,在某些情况下,匿名内部类和捕获lambda都隐式地添加了私有字段。例如,实例字段匿名类,它是对外部类实例的引用,而对于本地和静态匿名实例,则没有任何引用。但本地实例lambda不同:它有一个捕获变量类型的字段。如果最终它们的使用方式相同,为什么会有这种差异?为什么不能在本地匿名类的精确副本中构建本地lambda UPD: 一点样品 类来测试匿名类的字段 public class AnonymousExample { private Runnable instanceRunna
public class AnonymousExample {
private Runnable instanceRunnable = new Runnable() {
@Override
public void run() { }
};
private static Runnable staticRunnable = new Runnable() {
@Override
public void run() { }
};
public static void main(String[] args) throws IllegalAccessException {
Runnable localRunnable = new Runnable() {
@Override
public void run() { }
};
System.out.println("-- local anonymous class --");
print(localRunnable.getClass(), localRunnable);
System.out.println("\n-- instance anonymous class --");
AnonymousExample em = new AnonymousExample();
print(em.instanceRunnable.getClass(), em.instanceRunnable);
System.out.println("\n-- static member anonymous class --");
print(staticRunnable.getClass(), staticRunnable);
}
public static void print(Class<?> c, Object instance) throws IllegalAccessException {
System.out.println("- constructors -");
for (Constructor<?> constructor : c.getDeclaredConstructors()) {
System.out.println(constructor.toGenericString());
}
System.out.println("- fields -");
for (Field field : c.getDeclaredFields()) {
System.out.printf(" field name: %s,%n field type: %s,%n field value: %s%n",
field.getName(),
field.getType(),
field.get(instance));
}
}
}
类来测试捕获lambda的字段
public class CapturingLambdaExample {
private int x = 10;
private static int y = 20;
private Runnable instanceRunnable = () -> { System.out.println(x);};
private static Runnable staticRunnable = () -> { System.out.println(y);};
public static void main(String[] args) throws IllegalAccessException {
int z = 20;//effectively final
Runnable localRunnable = () -> { System.out.println(z);};
System.out.println("-- local capturing lambda --");
print(localRunnable.getClass());
System.out.println("\n-- instance capturing lambda --");
CapturingLambdaExample em = new CapturingLambdaExample();
print(em.instanceRunnable.getClass());
System.out.println("\n-- static capturing lambda --");
print(staticRunnable.getClass());
}
public static void print(Class<?> c) throws IllegalAccessException {
System.out.println("- constructors -");
for (Constructor<?> constructor : c.getDeclaredConstructors()) {
System.out.println(constructor.toGenericString());
}
System.out.println("- fields -");
for (Field field : c.getDeclaredFields()) {
System.out.printf(" field name: %s,%n field type: %s,%n ",
field.getName(),
field.getType());
}
}
}
匿名类和Lambda确实有一些区别
EnclosingClass.this.toString()
——拉姆达---
封入类别
EnclosingClass@eed1f14
---匿名类---
一些常见的处理方法
匿名类
匿名超类
封入类别
EnclosingClass@eed1f14
为什么本地捕获lambda需要隐式添加字段而匿名类不需要?
事实并非如此。在您的示例中,匿名类没有使用封闭方法中的任何字段
对于以下代码段
private static void start() throws IllegalAccessException {
int x = 5;
System.out.println("---- Anonymous class ----");
print(new Runnable() {
@Override
public void run() {
System.out.println(x);
}
}.getClass());
System.out.println("---- Lambda class ----");
Runnable r = () -> System.out.println(x);
print(r.getClass());
}
这是输出:
---- Anonymous class ----
- constructors -
EnclosingClassV2$1(int)
- fields -
field name: val$x,
field type: int,
---- Lambda class ----
- constructors -
private EnclosingClassV2$$Lambda$1/455659002(int)
- fields -
field name: arg$1,
field type: int,
注:
总之,生成lambda类采用了不同的策略,这样可以避免匿名类中涉及的额外类生成复杂性。好吧,匿名类是实际类,Lambda的设计是为了在需要/需要时不使用类就可以实现它们。您确定本地匿名类不会捕获此?您能分享您正在描述的场景示例和字段证据吗?@Savior Hi,我添加了描述question@markspace,我知道。这使得这个问题更加有趣。为什么本地捕获lambda需要隐式添加字段而匿名类不需要?谢谢。我忘了让匿名类捕获一些变量。在这种情况下,隐式添加的字段没有区别。
public class EnclosingClass {
public static void main(String[] args) {
new EnclosingClass().start();
}
private void run(Runnable runnable) {
runnable.run();
}
private void start() {
System.out.println("--- Lambda ---");
run(() -> {
System.out.println(this.toString());
System.out.println(super.toString()); // Use Object.toString
});
System.out.println("---Anonymous class---");
run(new AnonymousSuperClass() {
@Override
public void run() {
super.commonMethod();
System.out.println(this.toString());
System.out.println(super.toString());
System.out.println(EnclosingClass.this.toString());
System.out.println(EnclosingClass.super.toString()); // Use Object.toString
}
@Override
public String toString() {
return "Anonymous Class";
}
});
}
@Override
public String toString() {
return "Enclosing Class";
}
private static abstract class AnonymousSuperClass implements Runnable {
protected void commonMethod() {
System.out.println("Some common processing");
}
@Override
public String toString() {
return "Anonymous Super Class";
}
}
}
private static void start() throws IllegalAccessException {
int x = 5;
System.out.println("---- Anonymous class ----");
print(new Runnable() {
@Override
public void run() {
System.out.println(x);
}
}.getClass());
System.out.println("---- Lambda class ----");
Runnable r = () -> System.out.println(x);
print(r.getClass());
}
---- Anonymous class ----
- constructors -
EnclosingClassV2$1(int)
- fields -
field name: val$x,
field type: int,
---- Lambda class ----
- constructors -
private EnclosingClassV2$$Lambda$1/455659002(int)
- fields -
field name: arg$1,
field type: int,