我去搜索学习如何在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()方法,我只需要创建一次性方形类,并覆盖它们中的方法。它是否正确?
因此,匿名内部类与继承有关。我虽然不理解它的使用。也许,这是因为我之前从未使用过它们,或者因为我没有太多的编程经验。你可以给我举例或解释它什么时候有用吗?
更新:当我将匿名内部类的代码移动到IDE时,我了解到存在错误;显然," square"甚至没有继承矩形的字段。这不会使它更无用吗?
相当于:
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;
}
}
答案 0 :(得分:5)
所以我对匿名课程的理解是这样的:
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继承。 (并不是说我建议有另一种“非普通”的继承......)
答案 1 :(得分:2)
首先,广场可以访问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时会出现匿名类的相关性。你可以创建具体的类来为每个接口/抽象类实现逻辑的每一点,但这会产生大量的依赖关系,你仍然会遗漏一些逻辑。匿名类的一个很好的例子是使用谓词进行过滤。就像Google Guava
一样假设我有一个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和5 ...除了谓词检查之外,这个逻辑完全相同。因此,我们将创建一个界面
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 )
答案 2 :(得分:1)
要回答稍后的评论,&#34;当我编写新的子类时,它会继承这些私有实例变量。在匿名内部类的情况下,它没有。&#34;
子类从不&#34;继承&#34;超类的private
个字段(使用JLS术语)。但是,子类可能无论如何都可以引用那些私有字段,具体取决于它们所处的位置。如果子类在超类中声明 ,或者如果它们都嵌套在同一个顶级类中,则子类的方法仍然可以访问该字段;假设您的源文件C.java
只包含一个C
个类,private
中某处声明的C.java
字段仍可从C.java
中的大多数其他位置访问。< / p>
然而,在测试时,我发现了一些有趣的细微差别:
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
在 Foo1
之外宣布,则第二个println
将是非法的,因为现在bar1
根本不可见,因为它是私人的。这里的道德是&#34;继承&#34;和&#34;可见性&#34; (或&#34;访问&#34;)不是一回事。同样的事情适用于匿名内部类。如果您在私有实例字段可见的位置使用一个,那么您可以参考该字段;如果您在私有实例字段不可见的地方使用它,那么您就无法做到。为此目的,类声明的位置比类的类型(嵌套/内部/匿名)更重要。
假设我们删除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
本身引用封闭实例中的字段。