Java中的Kotlin?內(nèi)部類原理
Java 中的內(nèi)部類
這是一個 Java 內(nèi)部類的簡單實現(xiàn):
public class OutterJava { ? ?private void printOut() { ? ? ? ?System.out.println("AAA"); ? } ? ? ?class InnJava { ? ? ? ?public void printInn() { ? ? ? ? ? ?printOut(); ? ? ? } ? } }
外部類是一個私有方法,內(nèi)部類為什么可以訪問到外部類的私有方法呢?思考這個問題,首先要從它的字節(jié)碼入手,看看 JVM 到底對 java 文件做了什么。
字節(jié)碼分析流程是:
javac xxx.java
生成 class 文件。javap -c xxx.class
對代碼進行反匯編,可以生成可查看的代碼內(nèi)容。
通過 javac 命令生成 class 文件,此時會發(fā)現(xiàn)生成了兩個 class 文件,一個外部類 OtterJava 的,一個內(nèi)部類 InnJava 的。
OutterJava.class
OutterJava.class 反匯編后的代碼如下所示,這里面除了一個構(gòu)造方法,多生成了一個
Compiled from "OutterJava.java" public class java.OutterJava { ?public java.OutterJava(); ? ?Code: ? ? ? 0: aload_0 ? ? ? 1: invokespecial #2 ? ? ? ? ? ? ? ? ?// Method java/lang/Object."<init>":()V ? ? ? 4: return ? ?private void printOut(); ? ?Code: ? ? ? 0: getstatic ? ? #3 ? ? ? ? ? ? ? ? ?// Field java/lang/System.out:Ljava/io/PrintStream; ? ? ? 3: ldc ? ? ? ? ? #4 ? ? ? ? ? ? ? ? ?// String AAA ? ? ? 5: invokevirtual #5 ? ? ? ? ? ? ? ? ?// Method java/io/PrintStream.println:(Ljava/lang/String;)V ? ? ? 8: return ? ?static void access$000(java.OutterJava); ? ?Code: ? ? ? 0: aload_0 ? ? ? 1: invokespecial #1 ? ? ? ? ? ? ? ? ?// Method printOut:()V ? ? ? 4: return }
從反編譯出來的內(nèi)容來看,多了一個靜態(tài)的access$000(OutterJava)
方法,它的內(nèi)部調(diào)用了 printOut()
。
InnJava.class
Compiled from "OutterJava.java" class java.OutterJava$InnJava { ?final java.OutterJava this$0; ? ?java.OutterJava$InnJava(java.OutterJava); ? ?Code: ? ? ? 0: aload_0 ? ? ? 1: aload_1 ? ? ? 2: putfield ? ? ?#1 ? ? ? ? ? ? ? ? ?// Field this$0:Ljava/OutterJava; ? ? ? 5: aload_0 ? ? ? 6: invokespecial #2 ? ? ? ? ? ? ? ? ?// Method java/lang/Object."<init>":()V ? ? ? 9: return ? ?public void printInn2(); ? ?Code: ? ? ? 0: aload_0 ? ? ? 1: getfield ? ? ?#1 ? ? ? ? ? ? ? ? ?// Field this$0:Ljava/OutterJava; ? ? ? 4: invokestatic ?#3 ? ? ? ? ? ? ? ? ?// Method java/OutterJava.access$000:(Ljava/OutterJava;)V ? ? ? 7: return }
在 InnJava 的字節(jié)碼反編譯出來的內(nèi)容中,主要有兩個點需要注意:
- 構(gòu)造方法需要一個外部類參數(shù),并把這個外部類實例保存到了
this$0
中。 - 調(diào)用外部類私有方法,實際上是調(diào)用了
OutterJava.access$000
方法。
小結(jié):
在 Java 中,內(nèi)部類與外部類的關系是:
- 內(nèi)部類持有外部類的引用,作為內(nèi)部構(gòu)造參數(shù)傳入外部類實例,并保存到了內(nèi)部類的屬性
this$0
中。 - 內(nèi)部類調(diào)用外部類的私有方法,實際上是外部類生成了內(nèi)部實際調(diào)用私有方法的靜態(tài)方法
access$000
,內(nèi)部類可以通過這個靜態(tài)方法訪問到外部類中的私有方法。
Kotlin 中的內(nèi)部類
同樣的 Java 代碼,用 Kotlin 實現(xiàn):
class Outter { ? ?private fun printOut() { ? ? ? ?println("Out") ? } ? ? ?inner class Inner { ? ? ? ?fun printIn() { ? ? ? ? ? ?printOut() ? ? ? } ? } }
這里如果不加inner
關鍵字,printIn()
內(nèi)的printOut()
會報錯Unresolved reference: printOut
。
不加inner
關鍵字,反編譯后的字節(jié)碼:
public final class java/Outter$Inner { // ... public <init>()V ? L0 ? LINENUMBER 8 L0 ? ALOAD 0 ? INVOKESPECIAL java/lang/Object.<init> ()V ? RETURN ? L1 ? LOCALVARIABLE this Ljava/Outter$Inner; L0 L1 0 ? MAXSTACK = 1 ? MAXLOCALS = 1 // ... }
不加inner
關鍵字,內(nèi)部類的構(gòu)造方法是沒有外部類實例參數(shù)的。如果加上inner
,就和 Java 一樣:
// 加上了 inner 的構(gòu)造方法 public <init>(Ljava/Outter;)V ? L0 ? LINENUMBER 8 L0 ? ALOAD 0 ? ALOAD 1 ? PUTFIELD java/Outter$Inner.this$0 : Ljava/Outter; ? ALOAD 0 ? INVOKESPECIAL java/lang/Object.<init> ()V ? RETURN ? L1 ? LOCALVARIABLE this Ljava/Outter$Inner; L0 L1 0 ? LOCALVARIABLE this$0 Ljava/Outter; L0 L1 1 ? MAXSTACK = 2 ? MAXLOCALS = 2
而內(nèi)部類對于外部類私有方法的訪問,也是通過靜態(tài)方法access$XXX
來實現(xiàn)的:
public final static synthetic access$printOut(Ljava/Outter;)V ? L0 ? LINENUMBER 3 L0 ? ALOAD 0 ? INVOKESPECIAL java/Outter.printOut ()V ? RETURN ? L1 ? LOCALVARIABLE $this Ljava/Outter; L0 L1 0 ? MAXSTACK = 1 ? MAXLOCALS = 1
總結(jié)
在 Kotlin 中,內(nèi)部類持有外部類引用和通過靜態(tài)方法訪問外部類私有方法都是與 Java 一樣的。唯一的不同是,Kotlin 中需要使用 inner
關鍵字修飾內(nèi)部類,才能訪問外部類中的內(nèi)容。實質(zhì)是inner
關鍵字會控制內(nèi)部類的構(gòu)造方法是否帶有外部類實例參數(shù)。
到此這篇關于Java中的Kotlin 內(nèi)部類原理的文章就介紹到這了,更多相關Java Kotlin 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
IDEA中使用jclasslib插件可視化方式查看類字節(jié)碼的過程詳解
查看JAVA字節(jié)碼有兩種方式一種是使用 jdk命令 javap,還有一種就是 使用 插件了,今天給大家分享IDEA中使用jclasslib插件可視化方式查看類字節(jié)碼的過程詳解,感興趣的朋友跟隨小編一起看看吧2021-05-05Mybatis中注入執(zhí)行sql查詢、更新、新增及建表語句案例代碼
這篇文章主要介紹了Mybatis中注入執(zhí)行sql查詢、更新、新增以及建表語句,主要說明一個另類的操作,注入sql,并使用mybatis執(zhí)行,結(jié)合案例代碼詳解講解,需要的朋友可以參考下2023-02-02springboot的logging.group日志分組方法源碼流程解析
這篇文章主要為大家介紹了springboot的logging.group日志分組方法源碼流程解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12Spring MVC---數(shù)據(jù)綁定和表單標簽詳解
本篇文章主要介紹了Spring MVC---數(shù)據(jù)綁定和表單標簽詳解,具有一定的參考價值,有興趣的可以了解一下。2017-01-01老生常談spring boot 1.5.4 日志管理(必看篇)
下面小編就為大家?guī)硪黄仙U剆pring boot 1.5.4 日志管理(必看篇)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06