Overview
所謂的內(nèi)部類(lèi)即定義在類(lèi)內(nèi)部的類(lèi)拯辙,而包含這個(gè)內(nèi)部類(lèi)的類(lèi)則被稱(chēng)作外部類(lèi)。通常來(lái)說(shuō)內(nèi)部類(lèi)可以訪(fǎng)問(wèn)外部類(lèi)的私有成員,作為外部類(lèi)的內(nèi)部擴(kuò)展而存在吁脱。
Java 篇
靜態(tài)內(nèi)部類(lèi)
靜態(tài)內(nèi)部類(lèi)即以 static
關(guān)鍵字聲明的內(nèi)部類(lèi)。靜態(tài)內(nèi)部類(lèi)不屬于外部類(lèi)的成員彬向,使用上與普通的外部類(lèi)沒(méi)有什么區(qū)別兼贡。
定義一個(gè)靜態(tài)內(nèi)部類(lèi)
class Outter {
static class StaticInner {
}
}
創(chuàng)建靜態(tài)內(nèi)部類(lèi)的實(shí)例
Outter.StaticInner staticInner = new Outter.StaticInner();
匿名內(nèi)部類(lèi)
匿名內(nèi)部類(lèi)即沒(méi)有指明名字的內(nèi)部類(lèi),通常用于監(jiān)聽(tīng)器和線(xiàn)程的創(chuàng)建娃胆。
例:
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
}
});
以上 new Runnable()
即定義了一個(gè)沒(méi)有名字的實(shí)現(xiàn)了 Runnalbe
接口的內(nèi)部類(lèi)并立即創(chuàng)建該類(lèi)的實(shí)例作為 Thread
的構(gòu)造方法的參數(shù)遍希。
非靜態(tài)內(nèi)部類(lèi)
非靜態(tài)內(nèi)部類(lèi)屬于外部類(lèi),可以被看做為外部類(lèi)的一個(gè)成員里烦,和外部類(lèi)定義的普通方法和成員變量沒(méi)有什么區(qū)別凿蒜,所以在該內(nèi)部類(lèi)中可以訪(fǎng)問(wèn)外部類(lèi)的所有成員。也正是這個(gè)原因胁黑,創(chuàng)建內(nèi)部類(lèi)的實(shí)例時(shí)必須先有外部類(lèi)的實(shí)例废封。
定義一個(gè)非靜態(tài)內(nèi)部類(lèi)
如果內(nèi)部類(lèi)和外部類(lèi)擁有同名成員變量,如果直接使用變量名訪(fǎng)問(wèn)丧蘸,遵循就近原則漂洋,方法的只可能是內(nèi)部類(lèi)自身的成員。但是可以通過(guò) 外部類(lèi).this.變量名
的方式來(lái)明確指明需要調(diào)用的是外部類(lèi)成員而不是內(nèi)部類(lèi)的成員力喷。
例:
class Outter {
private String name;
public Outter(String name) {
this.name = name;
}
public class Inner {
private String name;
public Inner(String name) {
this.name = name;
}
public String desc() {
return Outter.this.name + "-" + name;
}
}
public void foo(Inner bar) {
System.out.println(bar.desc());
}
}
使用該內(nèi)部類(lèi)
Outter outter1 = new Outter("Outter1");
Outter.Inner inner1 = outter1.new Inner("Inner1");
Outter outter2 = new Outter("Outter2");
Outter.Inner inner2 = outter2.new Inner("Inner2");
System.out.println("outter1 is " + outter1.getClass()); // _innerclass.Outter
System.out.println("outter2 is " + outter2.getClass()); // _innerclass.Outter
System.out.println("inner1 is " + inner1.getClass()); // _innerclass.Outter$Inner
System.out.println("inner2 is " + inner2.getClass()); // _innerclass.Outter$Inner
outter1.foo(inner1); // Outter1-Inner1
outter1.foo(inner2); // Outter2-Inner2
以上可以看到 inner1
和 inner2
兩個(gè)通過(guò)不同外部類(lèi)實(shí)例創(chuàng)建的內(nèi)部類(lèi)對(duì)象并沒(méi)有什么區(qū)別刽漂,可見(jiàn)內(nèi)部類(lèi)是屬于外部類(lèi)的。
Groovy 篇
靜態(tài)內(nèi)部類(lèi)
使用同 Java
定義一個(gè)靜態(tài)內(nèi)部類(lèi)
class Outter {
static class StaticInner {
}
}
創(chuàng)建靜態(tài)內(nèi)部類(lèi)的實(shí)例
def staticInner = new Outter.StaticInner()
匿名內(nèi)部類(lèi)
匿名內(nèi)部類(lèi)使用也同 Java
例:
def thread = new Thread(new Runnable() {
@Override
void run() {
}
})
非靜態(tài)內(nèi)部類(lèi)
非靜態(tài)內(nèi)部類(lèi)使用也同 Java弟孟。但是爽冕,在 Groovy 中創(chuàng)建內(nèi)部類(lèi)的實(shí)例時(shí)語(yǔ)法與 Java 不同,使用的是 new 外部類(lèi).內(nèi)部類(lèi)(外部類(lèi)的實(shí)例 [, 內(nèi)部類(lèi)的構(gòu)造方法的參數(shù)])
披蕉,且此時(shí)指明內(nèi)部類(lèi)的構(gòu)造方法參數(shù)時(shí)不能使用帶名參數(shù)颈畸。
定義一個(gè)非靜態(tài)內(nèi)部類(lèi)
class Outter {
def name
static class StaticInner {
}
class Inner {
def name
Inner(def name) {
this.name = name
}
def desc = "${Outter.this.name}-${name}"
}
def foo(Inner bar) {
println(bar.desc)
}
}
使用該內(nèi)部類(lèi)
def outter1 = new Outter(name: "Outter1")
// Wrong!! 不能使用帶名參數(shù)
// def inner1 = new Outter.Inner(outter1, name: "Inner1")
def inner1 = new Outter.Inner(outter1, "Inner1")
def outter2 = new Outter(name: "Outter2")
def inner2 = new Outter.Inner(outter2, "Inner2")
println("outter1 is ${outter1.getClass()}") // _innerclass.Outter
println("outter2 is ${outter2.getClass()}") // _innerclass.Outter
println("inner1 is ${inner1.getClass()}") // _innerclass.Outter$Inner
println("inner2 is ${inner2.getClass()}") // _innerclass.Outter$Inner
outter1.foo(inner1) // Outter1-Inner1
outter1.foo(inner2) // Outter2-Inner2
由以上例子可見(jiàn)在 Groovy 中內(nèi)部類(lèi)也是屬于外部類(lèi)的乌奇。
Scala 篇
匿名內(nèi)部類(lèi)
匿名內(nèi)部類(lèi)使用也同 Java
val thread = new Thread(new Runnable {
def run(): Unit = {
}
})
非靜態(tài)內(nèi)部類(lèi)
Scala 中非靜態(tài)內(nèi)部類(lèi)與 Java 中有很大不同。Scala 中非靜態(tài)內(nèi)部類(lèi)是屬于外部類(lèi)的實(shí)例眯娱,而不是外部類(lèi)自身礁苗。
定義一個(gè)非靜態(tài)內(nèi)部類(lèi)
Scala 中內(nèi)部類(lèi)也可以通過(guò) 外部類(lèi).this.成員名
來(lái)訪(fǎng)問(wèn)外部類(lèi)的成員,或者也可以通過(guò)像如下的 outter =>
定義一個(gè) 外部類(lèi).this
的別名來(lái)訪(fǎng)問(wèn)徙缴。
class Outter(val name: String) {
outter =>
class Inner(val name: String) {
def desc = s"${Outter.this.name}-$name"
def desc2 = s"${outter.name}-$name"
}
def foo(bar: Inner): Unit = {
println(bar.desc)
}
}
使用該內(nèi)部類(lèi)
val outter1 = new Outter("Outter1")
val inner1 = new outter1.Inner("Inner1")
val outter2 = new Outter("Outter2")
val inner2 = new outter2.Inner("Inner2")
println(s"outter1 is ${outter1.getClass}") // _innerclass.Outter
println(s"outter2 is ${outter2.getClass}") // _innerclass.Outter
println(s"inner1 is ${inner1.getClass}") // _innerclass.Outter$Inner
println(s"inner2 is ${inner2.getClass}") // _innerclass.Outter$Inner
以上打印時(shí)可以看到 inner1
和 inner2
的類(lèi)型看起來(lái)是一樣的试伙。但是調(diào)用以下方法后會(huì)發(fā)現(xiàn)報(bào) type mismatch
錯(cuò)誤。
outter1.foo(inner1)
// Wrong!! type mismatch
// outter1.foo(inner2)
實(shí)際上這是由于 Scala 中存在著一種被稱(chēng)作 "路徑依賴(lài)類(lèi)型" 的概念于样,即 "A.this.B" 隨著 A 是不同的實(shí)例而不同疏叨。所以以上定義的 foo()
方法只能接受特定路徑的 Inner
類(lèi)的實(shí)例。
如果希望 foo()
方法接收所有 Outter
實(shí)例路徑下的 Inner
類(lèi)型穿剖,可以使用類(lèi)型投影蚤蔓。類(lèi)型投影使用符號(hào) #
來(lái)定義。
例:
將 foo()
方法換成以下形式
def foo(bar: Outter#Inner): Unit = {
println(bar.desc2)
}
再調(diào)用以下方法就不會(huì)報(bào)錯(cuò)了
outter1.foo(inner1)
outter1.foo(inner2)
Kotlin 篇
匿名內(nèi)部類(lèi)
匿名內(nèi)部類(lèi)使用也同 Java
val thread = Thread(Runnable {
})
非靜態(tài)內(nèi)部類(lèi)
Kotlin 中非靜態(tài)內(nèi)部類(lèi)與 Java 相似糊余。
定義一個(gè)非靜態(tài)內(nèi)部類(lèi)
Kotlin 中內(nèi)部類(lèi)使用語(yǔ)法 this@外部類(lèi).成員名
來(lái)調(diào)用外部成員秀又。
class Outter(val name: String) {
inner class Inner(val name: String) {
fun desc() = "${this@Outter.name}-${name}"
}
fun foo(bar: Inner) {
println(bar.desc())
}
}
使用該內(nèi)部類(lèi)
val outter1 = Outter("Outter1")
val inner1 = outter1.Inner("Inner1")
val outter2 = Outter("Outter2")
val inner2 = outter1.Inner("Inner2")
println("outter1 is ${outter1.javaClass}") // _innerclass.Outter
println("outter2 is ${outter2.javaClass}") // _innerclass.Outter
println("inner1 is ${inner1.javaClass}") // _innerclass.Outter$Inner
println("inner2 is ${inner2.javaClass}") // _innerclass.Outter$Inner
outter1.foo(inner1) // Outter1-Inner1
outter1.foo(inner2) // Outter1-Inner2
Summary
- Scala 中非靜態(tài)內(nèi)部類(lèi)屬于外部類(lèi)的對(duì)象而非外部類(lèi)本身,而其它三種語(yǔ)言中則屬于外部類(lèi)
- Scala 中可以定義外部類(lèi)this的別名
- Scala 與 Kotlin 沒(méi)有靜態(tài)外部類(lèi)
- Groovy 中內(nèi)部類(lèi)使用方法與 Java 基本一致贬芥,只是創(chuàng)建內(nèi)部類(lèi)語(yǔ)法的方式不一樣
- Kotlin 使用 this 的語(yǔ)法和其它三種語(yǔ)言都不一樣
文章源碼見(jiàn) https://github.com/SidneyXu/JGSK 倉(cāng)庫(kù)的 _17_innerclass
小節(jié)