內(nèi)部類
一個(gè)定義在另一個(gè)類中的類,叫作內(nèi)部類。
創(chuàng)建內(nèi)部類
public class Parcel {
class Contents {
private int i = 11;
public int value() { return i; }
}
}
在Parcel之外的類中想要?jiǎng)?chuàng)建Contents對(duì)象,需要 Parcel.Contents ( OuterClassName.InnerClassName )
內(nèi)部類自動(dòng)擁有對(duì)其外圍類所有成員和方法的訪問權(quán),即使是 private 的也可以。這是因?yàn)楫?dāng)內(nèi)部類對(duì)象被創(chuàng)建時(shí),一個(gè)外部類對(duì)象的引用被傳遞給這個(gè)內(nèi)部類對(duì)象了。
普通內(nèi)部類不能有 static 字段和方法
內(nèi)部類中的 .this 和 .new
.this
- 在內(nèi)部類中, *OuterClassName.this *(DotThis.this) 代表的是外部類對(duì)象的引用,單純的 this 就是內(nèi)部類對(duì)象的引用
.new
-
外部類對(duì)象.new 用于創(chuàng)建內(nèi)部類對(duì)象
public class DotNew { public class Inner {} public static void main(String[] args) { DotNew dn = new DotNew(); DotNew.Inner dni = dn.new Inner(); } }
方法和作用域中的內(nèi)部類
public class Parcel6 {
private void internalTracking(boolean b) {
if(b) {
class TrackingSlip {
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip() { return id; }
}
TrackingSlip ts = new TrackingSlip("slip");
String s = ts.getSlip();
}
// Can't use it here! Out of scope:
//- TrackingSlip ts = new TrackingSlip("x");
}
}
內(nèi)部類只能在自己的作用域中能被使用
- 方法和作用域中的內(nèi)部類使用的外部變量必須是final的
匿名內(nèi)部類
public interface Contents {
int value();
}
public class Parcel7 {
public Contents contents() {
return new Contents() { // Insert class definition
private int i = 11;
@Override
public int value() { return i; }
}; // Semicolon required
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Contents c = p.contents();
}
}
在創(chuàng)建 Contents 對(duì)象代碼之后插入一個(gè)類的聲明,這個(gè)插入的類就是匿名類(沒有具體名字)
- 這里可以看成創(chuàng)建了一個(gè)繼承自 Contents 的匿名類對(duì)象,然后通過前面的 new 表達(dá)式將匿名類對(duì)象向上轉(zhuǎn)型成 Contents 對(duì)象
- 匿名內(nèi)部類中使用的外部變量必須是final 的
- 匿名內(nèi)部類,因?yàn)槭悄涿?,不能顯式聲明構(gòu)造函數(shù)
匿名內(nèi)部類中的參數(shù)初始化
public class Parcel9 {
// Argument must be final or "effectively final"
// to use within the anonymous inner class:
public Destination destination(final String dest) {
return new Destination() {
private String label = dest;
@Override
public String readLabel() { return label; }
};
}
public static void main(String[] args) {
Parcel9 p = new Parcel9();
Destination d = p.destination("Tasmania");
}
}
可以看到第6行用到的外部變量是final的(jdk8之后不用明確寫final了,但還是不能被改變)
為什么必須是final的? 參考知乎 胖君 的回答:https://www.zhihu.com/question/21395848
嵌套類
static類型的內(nèi)部類叫做嵌套類。嵌套類對(duì)象沒有指向外部創(chuàng)建他的類的對(duì)象引用
- 要?jiǎng)?chuàng)建嵌套類對(duì)象,不需要外部類對(duì)象
- 不能從嵌套類的對(duì)象中訪問非靜態(tài)外部類的對(duì)象
- 嵌套類可以包含 static 字段和方法
- 接口中的內(nèi)部類自動(dòng)是 public 和 static 的
為什么要使用內(nèi)部類
-
最重要的原因是可以間接實(shí)現(xiàn)類的多繼承
我們知道java中的類只能單繼承,內(nèi)部類可以間接實(shí)現(xiàn)多繼承:
public class Demo1 { public String name() { return "BWH_Steven"; } } public class Demo2 { public String email() { return "xxx.@163.com"; } } public class MyDemo { private class test1 extends Demo1 { public String name() { return super.name(); } } private class test2 extends Demo2 { public String email() { return super.email(); } } public String name() { return new test1().name(); } public String email() { return new test2().email(); } public static void main(String args[]) { MyDemo md = new MyDemo(); System.out.println("我的姓名:" + md.name()); System.out.println("我的郵箱:" + md.email()); } }在MyDemo類中書寫了兩個(gè)內(nèi)部類,test1和test2 兩者分別繼承了Demo1和Demo2類,這樣MyDemo中就間接的實(shí)現(xiàn)了多繼承
-
使用匿名內(nèi)部類實(shí)現(xiàn)回調(diào)功能(jdk8之后可以使用lambda表達(dá)式)
當(dāng)有這么個(gè)需求,一個(gè)方法的參數(shù)是接口對(duì)象,因?yàn)榻涌诓荒苌蓪?duì)象,而單獨(dú)寫一個(gè)類來實(shí)現(xiàn)接口又太浪費(fèi)(因?yàn)檫@個(gè)方法可能只會(huì)被使用一次),這時(shí)匿名內(nèi)部類很容易實(shí)現(xiàn)這一需求interface Demo { void interfaceMethod(); } public class NiMingInnerClass { public void test(Demo demo) { demo.interfaceMethod(); } public static void main(String[] args) { NiMingInnerClass innerClass = new NiMingInnerClass(); innerClass.test(new Demo() { @Override public void interfaceMethod() { System.out.println("接口方法被調(diào)用了"); } }); } } -
父類和接口中存在同名方法,而直接Override這個(gè)方法會(huì)導(dǎo)致只能保留一個(gè)方法,想要既保留繼承自父類的實(shí)現(xiàn),又保留接口中的方法,這時(shí)選擇內(nèi)部類很容易達(dá)到需求
interface Demo2 { void test(); } abstract class SupClass { public void test() { System.out.println("SupClass test()"); } } public class SameNameMethod extends SupClass { @Override public void test() { super.test(); System.out.println("SameNameMethod test()"); } private class InnerClass implements Demo2{ @Override public void test() { System.out.println("Demo2 test()"); } } public InnerClass getInner() { return new InnerClass(); } public static void main(String[] args) { SameNameMethod test = new SameNameMethod(); test.test(); test.getInner().test(); } }