2

我去寻找学习如何在 Java 中执行 lambda 表达式,但我却遇到了困惑。所以我对匿名类的理解是这样的:

public class SomeObject {
   public static void main(String[] args) {
    ArrayList list = new ArrayList();
    list.add(new SomeObject());
   }

}

之前看到过匿名内部类这个词,但当时还不知道什么是正规匿名类。我看到的很多线程和视频似乎都将匿名内部类称为“匿名类”。它们是同义词吗?我对匿名内部类的理解是:

 public class Rectangle {
 private double length;
 private double width;
 private double perimeter;

    public void calculatePerimeter() {
    perimeter = (2*length) +(2*width);
   }

     public static void main(String[] args) {
       Rectangle square = new Rectangle() {
        public void calculatePerimeter() {
            perimeter = 4*length;
        }
    };
   }

  }

所以本质上,不必为 Square 编写一个子类,然后重写 calculatePerimeter() 方法,我可以只创建一个一次性的 square 类,然后重写他们的方法。它是否正确?

因此,匿名内部类与继承有关。我不明白它的用途。也许是因为我以前从未使用过它们,或者因为我没有太多的编程经验。你可以给我例子或解释什么时候有用吗?

更新:当我将匿名内部类的代码移动到 IDE 时,我了解到有错误;显然,“正方形”甚至不继承矩形的字段。这不是让它更没用吗?

等价物是:

public class Rectangle {
 private double length;
 private double width;
 private double perimeter;

    public void calculatePerimeter() {
    perimeter = (2*length) +(2*width);
   }
 }


public class Square extends Rectangle {
   @Override   
   public void calculatePerimeter() {
      perimeter = 4*getLength();
   }

  public double getLength() {
    return length;
  }



    }
4

3 回答 3

6

所以我对匿名类的理解是这样的:

public class SomeObject {
   public static void main(String[] args) {
    ArrayList list = new ArrayList();
    list.add(new SomeObject());
   }
}

那里没有匿名类。该类SomeObject有一个名字......因此它不是匿名的。事实上,它只是一个普通的(非嵌套、非内部、非匿名)Java 类。


之前看到过匿名内部类这个词,但当时还不知道什么是正规匿名类。

没有“常规匿名类”之类的东西。所有 Java 匿名类都是“内部的”。

正如 JLS 所说:

“内部类是没有显式或隐式声明为静态的嵌套类。

内部类包括本地(§14.3)、匿名(§15.9.5)和非静态成员类(§8.5)。”


因此,匿名内部类与继承有关。

匿名内部类确实涉及继承,但这并不是使它们“内部”的原因。往上看。


我的意思是“list.add(我的意思是“list.add(new SomeObject());”。一直以来,我认为你添加到 ArrayList 的对象被称为匿名类,因为我们没有命名它.);"。一直以来,我一直认为您添加到 ArrayList 的对象被称为匿名类,因为我们没有命名它。

你不正确。对象不是类1

new SomeObject()是创建一个对象,而不是一个类。但这很正常。对象/实例没有名称……就 JLS 而言。

现在变量和字段有了名字......但变量不是对象/实例或类。它们是名称和可以保存对对象的引用的插槽之间的绑定(如果这是类型声明所允许的)。

1 - 除了...的实例,即使从理论的角度来看,对象实际上java.lang.Class也不是类/类型。


或者它只是被称为一个匿名对象而我把两个混淆了?

没有。对象没有名称。所有 Java 对象都是“匿名的”。这不是一个有用的区别。(见上文我谈论变量的地方......)


至于您的Rectangle/Square示例,它们与匿名类、内部类、嵌套类或类似的东西无关。它们只是顶级类,使用普通的 Java 继承。(并不是说我暗示还有另一种“非普通”的继承......)

于 2014-03-29T01:44:01.817 回答
2

首先 - square 可以访问 Rectangle 中的字段。你需要标记它们protected不是private

public class Rectangle {
    protected double length;
    protected double width;
    protected double perimeter;

    public void calculatePerimeter() {
        perimeter = (2*length) +(2*width);
    }

    public static void main(String[] args) {
        Rectangle square = new Rectangle() {
            public void calculatePerimeter() {
                perimeter = 4*length;
            }
        };
    }

}

以下是对内部类、匿名和本地的一些很好的描述

还有两种额外的内部类。您可以在方法体中声明内部类。这些类称为本地类。您还可以在方法体中声明内部类,而无需命名该类。这些类称为匿名类。

局部类是在一个块中定义的类,它是一组在平衡大括号之间的零个或多个语句。您通常会发现在方法体中定义的本地类。

匿名类使您的代码更简洁。它们使您能够同时声明和实例化一个类。它们类似于本地类,只是它们没有名称。如果您只需要使用一次本地类,请使用它们。

我认为当您设计 API 时,匿名类的相关性就出现了。您可以创建具体的类来为每个接口/抽象类实现每一点逻辑,但这会产生大量的依赖关系,并且您仍然会丢失一些逻辑。匿名类的一个很好的例子是使用谓词进行过滤。就像在谷歌番石榴

假设我有一个List<Integer> 并且我想过滤数字删除 1 并返回一个新列表

public static List<Integer> filter(List<Integer> input) {
   List<Integer> rtn = new ArrayList<Integer>();
   for( Integer i : input) {
      if(i != 1) rtn.push(i);
   }
   return rtn;
} 

现在假设我要过滤掉 1 和 2

public static List<Integer> filter(List<Integer> input) {
   List<Integer> rtn = new ArrayList<Integer>();
   for( Integer i : input) {
      if(i != 1 && i != 2) rtn.push(i);
   }
   return rtn;
} 

现在让我们说 3 和 5s ......除了谓词检查之外,这个逻辑完全相同。所以我们将创建一个接口

interface FilterNumber {
    public boolean test(Integer i);
}

class Filter1s implements FilterNumber {
    public Filter1s(){};
    public boolean test(Integer i) { return i != 1; }
} 


public static List<Integer> filter(List<Integer> input, FilterNumber filterNumber) {
   List<Integer> rtn = new ArrayList<Integer>();
   for( Integer i : input) {
      if(filterNumber.test(i)) rtn.push(i);
   }
   return rtn;
} 

filter(list, new Filter1s());

正如你所看到的组合,这也变得乏味。只允许api的用户定义他们想要执行的逻辑会更容易,如果只需要一次就使用匿名类

filter(list, new FilterNumber() {
    @Override
    public boolean test(Integer i) {
        return i != 1 && i != 3 && i != 7; 
    }
});

并且扩展到 Lambdas,消除所有的臃肿不是更容易吗i != 1

list.stream().filter( i -> i != 1 )
于 2014-03-29T02:40:45.790 回答
1

回答后来的评论,“当我编写一个新的子类时,它继承了那些私有实例变量。在匿名内部类的情况下,它没有。”

子类从不“继承”private超类的字段(使用 JLS 术语)。但是,子类可能无论如何都能够引用这些私有字段,具体取决于它们所在的位置。如果子类在超类中声明或者它们都嵌套在同一个顶级类中,则子类的方法仍然可以访问该字段;假设你有一个C.java只有一个类Cprivate源文件,C.javaC.java.

然而,在测试这个时,我发现了一些有趣的细微差别:

class Foo1 {    
    private int bar1;
    public static class Foo2 extends Foo1 {
        public void p() {
            System.out.println(bar1);               // illegal
            System.out.println(((Foo1)this).bar1);  // works
        }
    }
}

bar1是可见的,即使它是超类中的私有字段;它不是继承的,但您可以通过告诉编译器将Foo2对象视为Foo1. 但是仅仅引用bar1本身是失败的;Java 将此解释为尝试获取bar1封闭实例(不是超类)的,但Foo2它是静态的,因此没有封闭实例。

请注意,如果在外部Foo2声明,则第二个将是非法的,因为 now根本不可见,因为它是私有的。这里的寓意是“继承”和“可见性”(或“访问”)不是一回事。同样的事情也适用于匿名内部类。如果在私有实例字段可见的地方使用一个,则可以参考该字段;如果您在私有实例字段不可见的地方使用它,则不能。为此,类声明的位置比类的类型(嵌套/内部/匿名)更重要。 Foo1printlnbar1

假设我们去掉static关键字并使其成为内部类:

public class Foo1 {

    private int bar1;

    public Foo1(int x) {
        bar1 = x;
    }

    public class Foo2 extends Foo1 {

        public Foo2(int x) {
            super(x * 10);
        }

        public void show() {
            System.out.println("bar1 = " + bar1);
            System.out.println("((Foo1)this).bar1 = " + ((Foo1)this).bar1);
            System.out.println("Foo1.this.bar1 = " + Foo1.this.bar1);
        }
    }
}

public class Test64 {

    public static void main(String[] args) {
        Foo1 f1 = new Foo1(5);
        Foo1.Foo2 f2 = f1.new Foo2(6);
        f2.show();
    }

}    

现在一个Foo2对象也是一个Foo1;但由于它是一个内部类,一个Foo2实例也有一个封闭的实例,它是一个不同 Foo1的对象。当我们创建我们的Foo2时,它使用超类构造函数将超类设置bar1为 60。但是,它还有一个封闭实例,其bar1值为 5。 show()显示以下输出:

bar1 = 5
((Foo1)this).bar1 = 60
Foo1.this.bar1 = 5

因此bar1,它本身就是指封闭实例中的字段。

于 2014-03-30T02:00:11.733 回答