本文主要还是记录下内部类的基础语法,具体的作用等接触更多的代码之后回来小结
内部类,顾名思义就是在一个类的内部再定义一个类
public class Outer { private String id; public Outer(String id) { this.id = id; } /** * 内部类可以是任意的访问权限 */ public class Inner { // 可以任意的访问外部的属性 private String innerId = "inner" + id; } }
private
,default
,protected
,public
)private
,所以内部类的作用之一就是可以对外屏蔽类的存在Iterator
就是一个比较典型的例子List<String> list = new ArrayList<>(); Iterator<String> iterator = list.iterator();对于客户端来说,你拿到的是一个
Iterator
,而非ArrayList
的内部类对象Itr
,你能访问的也只有Iterator
接口所提供的方法ArrayList
提供了迭代器的功能,但是完全对客户端屏蔽其实现这一小结主要就是说明访问外部属性以及创建内部类对象的方法
普通的内部类(非静态内部类)之所以能够访问外部类的所有属性,肯定是因为有一个应用指向了外部类
如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟 .this --- 《OnJava8》
.this
Outer.this.getId()
,其实直接调用getId()即可
public class Outer { private String id; public Outer(String id) { this.id = id; } private String getId() { return this.id; } private void setId(String id) { this.id = id; } /** * 内部类可以是任意的访问权限 */ public class Inner { // 可以任意的访问外部的属性 private String innerId = "inner" + id; public String getOutId() { return getId(); } public void setId(String id) { Outer.this.setId(id); } } }
创建内部类对象时必须要使用外部类的对象进行创建
public class OuterTest { public static void main(String[] args) { Outer outer = new Outer("张三"); Outer.Inner inner = outer.new Inner(); System.out.println("inner class getId: " + inner.getId()); } }
有时一个内部类的对象只在某个方法中使用,我们可以在方法中定义内部类
局部内部类的另一个优点就是可以访问方法中的局部变量(必须事实上是final的,即在匿名类外部赋值后不能再改变了)
在局部内部类的基础上再进一步,有时我们不需要创建多个内部类对象,只需要一个,那么使用匿名内部类会更加方便
例如我们在Java多线程中使用Runnable
接口时就是如此。
假如说有一个计算接口
public interface Calculate { int cal(Integer num1, Integer num2); }
我们想实现一个加法器
public class Outer3 { class Adder implements Calculate{ @Override public int cal(Integer num1, Integer num2) { return num1 + num2; } } public Calculate getAdder() { return new Adder(); } }
测试一下:
Outer3 outer3 = new Outer3(); Calculate adder = outer3.getAdder(); int r1 = adder.cal(1,2); // 3
public class Outer3 { public Calculate getAdder() { return new Calculate() { @Override public int cal(Integer num1, Integer num2) { return num1 + num2; } }; } }
同样的测试:
Outer3 outer3 = new Outer3(); Calculate adder = outer3.getAdder(); int r1 = adder.cal(1,2); // 3
Calculate
只有一个接口,所以我们还可以用Java8的lambda表达式再简化一下public class Outer3 { public Calculate getAdder() { return (num1, num2) -> num1 + num2; } }
测试方法还是不变
Outer3 outer3 = new Outer3(); Calculate adder = outer3.getAdder(); int r1 = adder.cal(1,2); // 3
Calculate adder = (num1, num2) -> num1 + num2; int calRes = adder.cal(1,2); // 3
匿名内部类因为没有名字,所以你没有办法通过构造函数进行初始化,但我们还是有一些其他的方法来达到相同的效果
public abstract class AbstractCalculate implements Calculate { public int baseNum; public AbstractCalculate(int i) { baseNum = i; } }
测试一下
// 此处的 1 可修改为从外部传参 Calculate adder = new AbstractCalculate(1) { @Override public int cal(Integer num1, Integer num2) { return num1 + num2 + baseNum; } }; int res = adder.cal(1,2); // 4
即通过父类来进行初始化并使用父类的成员
public class Outer3 { public Calculate getAdder() { return (num1, num2) -> num1 + num2; } // 这里我们在初始化的时候做一些复杂的事情,比如初始化一个对象 public Calculate getAdderWithInit(int base) { Calculate adder = new Calculate() { Calculate _adder; { _adder = getAdder(); } @Override public int cal(Integer num1, Integer num2) { return _adder.cal(num1,num2) + base; } }; return adder; } }
测试一下:
Calculate adder = new Outer3().getAdderWithInit(2); int res = adder.cal(1,2); // 5
有时我们只是为了把一个类隐藏在另一个类中,并不需要内部类应用外部类的对象,此时我们就可以将内部类声明为static的
普通的内部类对象隐式地保存了一个引用,指向创建它的外部类对象。然而,当内部类是 static 的时,就不是这样了。
嵌套类(静态内部类)意味着:
- 创建嵌套类的对象时,不需要其外部类的对象。
- 不能从嵌套类的对象中访问非静态的外部类对象。
嵌套类与普通的内部类还有一个区别:
普通内部类的字段与方法,只能放在类的外部层次上,所以普通的内部类不能有 static 数据和 static 字段,也不能包含嵌套类。
但是嵌套类可以包含所有这些东西:
嵌套类可以作为接口的一部分。你放到接口中的任何类都自动地是 public 和 static 的。因为类是 static 的,只是将嵌套类置于接口的命名空间内,这并不违反接口的规则。你甚至可以在内部类中实现其外部接口
如果你想要创建某些公共代码,使得它们可以被某个接口的所有不同实现所共用,那么使用接口内部的嵌套类会显得很方便
---- 《OnJava8》
使用内部类命名可以避免命名重复的问题
例如Element这样一个类,你在很多类中可能都会使用到,为了避免重复你可能会使用AElement,BElement这种方式,但是使用内部类则可以使代码结构更加清晰一些
如果不使用外部类,我们就需要使用组合的方式来实现对象的复用,相比使用内部类的缺点:
即在访问权限中提到过的,可以隐藏内部类,当我的内部类实现了某个接口时,则相当于外部类隐藏了接口的实现,比较典型的就是Iterator
每个内部类都能独立地继承自一个(接口的)实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。 ---- 《OnJava8》
《OnJava8》
《Java核心技术:卷1》