Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cocoa/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么lambda和anonymous类有不同的隐式添加字段?_Java_Lambda - Fatal编程技术网

Java 为什么lambda和anonymous类有不同的隐式添加字段?

Java 为什么lambda和anonymous类有不同的隐式添加字段?,java,lambda,Java,Lambda,众所周知,在某些情况下,匿名内部类和捕获lambda都隐式地添加了私有字段。例如,实例字段匿名类,它是对外部类实例的引用,而对于本地和静态匿名实例,则没有任何引用。但本地实例lambda不同:它有一个捕获变量类型的字段。如果最终它们的使用方式相同,为什么会有这种差异?为什么不能在本地匿名类的精确副本中构建本地lambda UPD: 一点样品 类来测试匿名类的字段 public class AnonymousExample { private Runnable instanceRunna

众所周知,在某些情况下,匿名内部类和捕获lambda都隐式地添加了私有字段。例如,实例字段匿名类,它是对外部类实例的引用,而对于本地和静态匿名实例,则没有任何引用。但本地实例lambda不同:它有一个捕获变量类型的字段。如果最终它们的使用方式相同,为什么会有这种差异?为什么不能在本地匿名类的精确副本中构建本地lambda

UPD:

一点样品

类来测试匿名类的字段

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确实有一些区别

  • 匿名类可以扩展另一个类,也可以有多个方法。因此,需要使用关键字this和super分别访问它自己的成员和它的超类成员。此外,如果匿名类需要访问封闭类&封闭类的父成员,则必须使用限定的this和super关键字。示例:
    EnclosingClass.this.toString()

  • 另一方面,Lambda只能用一种方法实现接口。它没有其他成员,因此关键字this和super可以用来指代封闭类及其父类的成员

  • 引用

    与匿名类声明中出现的代码不同 名称以及lambda正文中出现的this和super关键字, 除了引用声明的可访问性之外,它们都是相同的 就像周围的环境一样

    示例代码:

    样本输出:

    ——拉姆达---
    封入类别
    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类采用了不同的策略,这样可以避免匿名类中涉及的额外类生成复杂性。

    好吧,匿名类是实际类,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,