內(nèi)部類的類型
普通內(nèi)部類(非靜態(tài))
public class Outer {
class Inner{}
}
實際例子:
public interface Iterator<T> {
boolean hasNext();
T next();
}
public class MyList<T> {
private Object[] list;
private int size;
private int currentSize;
public MyList(int size){
list=new Object[size];
this.size=size;
}
public void add(T element){
if(currentSize<size){
list[currentSize++]=element;
}else{
//拋出異常
}
}
public void remove(){
if(currentSize>0){
list[--currentSize]=null;
}else{
//拋出異常
}
}
Iterator interator(){
return new Itr<T>();
}
class Itr<T> implements Iterator<T>{
int cursor; // index of next element to return
@Override
public boolean hasNext() {
return cursor<currentSize;
}
@Override
public T next() {
if(hasNext()){
// lastRet++;
int i=cursor;
cursor++;
return (T)list[i];
}
return null;
}
}
}
測試代碼
public static void main(String[] args){
MyList<String> list=new MyList<>(10);
list.add("today");
list.add("is");
list.add("week");
list.add("?");
Iterator iterator=list.interator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
運行結(jié)果:
today
is
week
?
這是一個典型的迭代器模式。從上面例子可以看出,內(nèi)部可以訪問外圍類的成員變量,當然也可以訪問成員方法,因為生成內(nèi)部類的對象的時候就與制造它的外圍類對象就有了聯(lián)系,內(nèi)部類有外圍類的所有元素的訪問權(quán)。這是怎么做到的呢?
protected void test(){
Outer outer=new Outer();
// Outer.Inner inner=new Outer().new Inner();
Outer.Inner inner=outer.new Inner();
inner.getI();
}
class Outer{
int i=10;
class Inner{
public void getI(){
System.out.println("outer class i="+i);
}
}
}
從上面看出得到一個內(nèi)部實例的例子,創(chuàng)建一個內(nèi)部類實例的時候必須要得到一個外圍類的實例,然后再按照outer.new Inner()來得到一個內(nèi)部類對象。擁有外部類對象之前是不可能創(chuàng)建內(nèi)部類實例的(除非是靜態(tài)內(nèi)部類,稍后說)。當通過外圍類對象創(chuàng)建內(nèi)部類實例的時候,秘密捕獲了這個指向外圍類的對象,當在內(nèi)部類訪問外圍類的成員的就是用的這個對象調(diào)用外圍的對象。幸運的是,編輯器幫你處理了這些細節(jié)。
public void getI(){
//完整調(diào)用語句
System.out.println("outer class i="+Outer.this.i);
}
在java中比較常見的是集合類中使用迭代器。
局部內(nèi)部類
public class Outer {
public void test(){
class Inner{}
new Inner();
}
// class Inner{}
}
可以在任意的方法或作用域定義內(nèi)部類。我們可以創(chuàng)建一個類來輔助解決方案,而又不想這個類是共用的。如上,Inner定義在test方法中,所以在test方法外不能直接訪問Inner類。
class Outer{
public IInner getInner(){
class Inner implements IInner{
@Override
public void innerPrint() {
System.out.println("innerclass in innerPrint");
}
}
return new Inner();
}
}
interface IInner{
void innerPrint();
}
return 語句中用了向上轉(zhuǎn)型,當使用此方法時得到一個基類對象的引用就可以很好的隱藏實現(xiàn)細節(jié),通過這種方式可以完全阻止任何任何依賴于類型的編碼。也不是意味著getInner方法執(zhí)行結(jié)束了之后Inner就不可用了。你可以在同一個子目錄下的任意類使用Inner進行類標識,這并不會有命名沖突。
匿名內(nèi)部類
將上面例子修改下
class Outer{
public IInner getInner(){
return new IInner() {
@Override
public void innerPrint() {
System.out.println("innerclass in innerPrint");
}
};
}
匿名內(nèi)部類就是沒有名字的類,自動向上轉(zhuǎn)型為基類,在匿名內(nèi)部類使用了默認的構(gòu)造器來生成對象,那如果你的基類需要一個有參數(shù)的構(gòu)造器怎么辦呢?
public class Parcle {
public Wrapping wrapping(int x){
return new Wrapping(x){
public int value(){
return super.value()*47;
}
};
}
}
public class Wrapping {
private int i;
public Wrapping(int x){
i=x;
}
public int value(){
return i;
}
}
只需要地傳遞合適的參數(shù)給基類的構(gòu)造器即可,這里是將x給new Wrapping(x),盡管Wrapping只是一個具有實現(xiàn)的普通類,但它還是被其導(dǎo)出類當作公共"接口"來使用。匿名內(nèi)部類與正規(guī)的繼承相比有些受限,因為匿名內(nèi)部類即可以擴展類,也可以實現(xiàn)接口,但是不能兩者兼?zhèn)?。而且如果實現(xiàn)接口也只能實現(xiàn)一個接口。
就像在android里面給View添加點擊事件監(jiān)聽都是實現(xiàn)一個匿名內(nèi)部類,如果在一個方法中定義,而想訪問方法里的局部變量,那么由于作用域的特性必需在局部變量前加上final,使其成為常量。
靜態(tài)內(nèi)部類
如果不需要內(nèi)部類與外圍類之間有聯(lián)系,那么可以將內(nèi)部類聲明為static。這樣內(nèi)部類的創(chuàng)建不需要依賴于外部類,因此靜態(tài)內(nèi)部類可能訪問外圍類的靜態(tài)成員。它沒有一個指向外圍類的引用。靜態(tài)內(nèi)部類與非靜態(tài)內(nèi)部類還有一個區(qū)別,普通內(nèi)部類的字段和方法,只能放在類的外部層次上,所以普通的內(nèi)部類不能有static和static 字段,也不能包含靜態(tài)內(nèi)部類,但是靜態(tài)內(nèi)部類可以。
接口內(nèi)部的類
正常情況下,不能在接口 內(nèi)部放置任何代碼,但靜態(tài)內(nèi)部類可以作為接口的一部份,放到接口中的任何類都是public和static的。如果你想要創(chuàng)建某此公共代碼,使得它們可以被某個接口的所有不同實現(xiàn)所共用,那么使用接口內(nèi)部的嵌套類會很方便。要做測試的話可以在每個類中都寫一個main()方法,用來測試這個類。這樣做有一個缺點,那就是必須帶著那些已編譯過的額外代碼。那我們就可以用嵌套類來放置測試代碼。
public class TestBed{
public void f(){
System.out.println("f()");
}
public static class Tester{
public static void main(String[] args){
TestBed t=new TestBed();
t.f();
}
}
}
這生成了一個獨立的類TestBed$Tester,不想在發(fā)布的產(chǎn)品中包含它,在將產(chǎn)品打包前可以簡單地刪除TestBed$Tester.class
說說內(nèi)部類的其它特性
多重嵌套內(nèi)部類訪問外部類的成員
一個內(nèi)部類被嵌套多少層并不重要,它能透明地訪問所有它所嵌入的外圍類的所有成員。
class MNA{
private void f(){}
class A{
private void g(){}
public class B{
void h(){
f();
g();
}
}
}
}
閉包
閉包是一個可調(diào)用的對象,它記錄了一些信息,這些信息來自于創(chuàng)建它的作用域。通過這個定義,可以看到非靜態(tài)內(nèi)部類是面向?qū)ο蟮拈]包,因為它不僅包含外圍類對象的信息,還自動擁有一個指向此外圍類對象的引用,在此作用域內(nèi),內(nèi)部類有權(quán)操作所有的成員,包括private成員。
內(nèi)部類的繼承
因為非靜態(tài)內(nèi)部類的構(gòu)造器必須連接到指向其外圍類的引用,所以在繼承內(nèi)部類的時候情況會有些復(fù)雜。問題在于指向外圍類對象的“秘密的”引用必須被初始化,而在導(dǎo)出類中不再存在可連接的默認對象。要解決這個問題,必須使用特殊的語法來明確說清楚它們之間的關(guān)聯(lián):
class WithInner{
class Inner{}}
public class InheritInner extends WithInner.Inner{
InheritInner(WithInner wi){
wi.super();
}
public static void main(String[] args){
WithInner wi=new WithInner();
InheritInner ii=new InheritInner(wi);
}
}
可以看到InheritInner只繼承自內(nèi)部類,而不是外圍類,但是當要生成一個構(gòu)造器時,默認的構(gòu)造器并不算好,而且不能只是傳遞一個指向外圍類的引用 。此外,必須在構(gòu)造器中使用如下語法:
wi.super();
內(nèi)部類可以被覆蓋嗎
public class Egg {
private Yolk y;
protected class Yolk{
public Yolk(){
System.out.println("Egg.Yolk()");
}
}
public Egg(){
System.out.println("New Egg()");
y=new Yolk();
}
}
public class BigEgg extends Egg {
public class Yolk{
public Yolk(){
System.out.println("BigEgg.Yolk()");
}
}
public static void main(String[] args){
new BigEgg();
}
}
運行結(jié)果
New Egg()
Egg.Yolk()
這個例子說明,當繼承了某個外圍類的時候,內(nèi)部類并沒有發(fā)生什么特別神奇的變化。這兩個內(nèi)部類是完全獨立的個體,各自在自己的命名空間里。當然,明確地繼承某個內(nèi)部類也是可以的。
內(nèi)部類標識符
由于每個類都會產(chǎn)生一個.class文件,其中包含了如何創(chuàng)建該類型的對象的全部信息,當然內(nèi)部類也會產(chǎn)生一個.class文件,這些類文件 的命名有嚴格的規(guī)則,外圍類的名字加上$再加上內(nèi)部類的名字,如果是匿名內(nèi)部類則是外圍類名加$加上數(shù)字。