本文系 Creating JVM language 翻譯的第 16 篇坦康。
原文中的代碼和原文有不一致的地方均在新的代碼倉(cāng)庫(kù)中更正過(guò)攻泼,建議參考新的代碼倉(cāng)庫(kù)火架。
源碼
OOP 和 statics
面向?qū)ο笳Z(yǔ)言中最大的優(yōu)點(diǎn)是啥?我個(gè)人認(rèn)為是多態(tài)忙菠, 如何實(shí)現(xiàn)多態(tài)距潘,使用繼承唄,可以在靜態(tài)語(yǔ)義下使用繼承么只搁?肯定不行呀音比。
在我認(rèn)為,靜態(tài)語(yǔ)義違反了面向?qū)ο蟮乃枷肭馔铮粦?yīng)該出現(xiàn)在面向?qū)ο笾卸呆妫瑸榱吮苊馐褂枚鄳B(tài)稽犁,你可以用單例呀。
因此骚亿,為何有 static 情況下已亥,Java 還聲稱自己是面向?qū)ο蟮哪兀课艺J(rèn)為来屠,Java 是為了迎合 C++ 更加方便的接納 Java 才引入的歷史因素虑椎。
去掉 static
直到上一篇博客,Enkel 中一直存在 static俱笛。包括 main 方法和其他的靜態(tài)方法捆姜,這么是為了方便實(shí)現(xiàn)語(yǔ)言的其他特性,比如變量迎膜,條件表達(dá)式泥技,循環(huán),方法調(diào)用磕仅,然后才轉(zhuǎn)成 OO珊豹。
那么我們來(lái)實(shí)現(xiàn)沒(méi)有靜態(tài)的 OO 吧。
main 方法
static main 方法需要 Java 程序員手動(dòng)編寫(xiě)榕订。Enkel 是這樣處理的:
- 編譯器自動(dòng)生成
- 在 main 方法中店茶,用默認(rèn)的構(gòu)造器創(chuàng)建一個(gè)對(duì)象
- 然后調(diào)用 start 方法
- 程序員需要提供 start 方法定義
private Function getGeneratedMainMethod() {
FunctionParameter args = new FunctionParameter("args", BultInType.STRING_ARR, Optional.empty());
FunctionSignature functionSignature = new FunctionSignature("main", Collections.singletonList(args), BultInType.VOID);
ConstructorCall constructorCall = new ConstructorCall(scope.getClassName());
FunctionSignature startFunSignature = new FunctionSignature("start", Collections.emptyList(), BultInType.VOID);
FunctionCall startFunctionCall = new FunctionCall(startFunSignature, Collections.emptyList(), scope.getClassType());
Block block = new Block(new Scope(scope), Arrays.asList(constructorCall,startFunctionCall));
return new Function(functionSignature, block);
}
start 方法是非靜態(tài)的,但其實(shí)是 main 方法的一種變體劫恒。
INVOSTATIC vs INVOKEVIRTUAL
在第七部分忽妒,我用了 INVOKESTATIC 來(lái)做方法調(diào)用,是時(shí)候換成 INVOKEVIRTUAL 了兼贸。
INVOKEVIRTUAL 有一點(diǎn)和 INVOKESTATIC 有很大的差異段直,INVOKEVIRTUAL 需要一個(gè)所有者,INVOKESTATIC 從棧中出棧參數(shù)溶诞,INVOKEVIRTUAL 首先是把所有者出棧鸯檬,然后才是出棧參數(shù)。
如果沒(méi)有顯示的提供所有者信息螺垢,默認(rèn)用 this喧务。
//Mapping antlr generated FunctionCallContext to FunctionCall
@Override
public Expression visitFunctionCall(@NotNull EnkelParser.FunctionCallContext ctx) {
//other stuff
boolean ownerIsExplicit = ctx.owner != null;
if(ownerIsExplicit) {
Expression owner = ctx.owner.accept(this);
return new FunctionCall(signature, arguments, owner);
}
ClassType thisType = new ClassType(scope.getClassName());
return new FunctionCall(signature, arguments, new VarReference("this",thisType)); //pass "this" as a owner
}
//Generating bytecode using mapped FunctionCall object
public void generate(FunctionCall functionCall) {
functionCall.getOwner().accept(this); //generate owner (pushses it onto stack)
generateArguments(functionCall); //generate arguments
String functionName = functionCall.getIdentifier();
String methodDescriptor = DescriptorFactory.getMethodDescriptor(functionCall.getSignature());
String ownerDescriptor = functionCall.getOwnerType().getInternalName();
//Consumes owner and arguments off the stack
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, ownerDescriptor, functionName, methodDescriptor, false);
}
示例
如下 Enkel 代碼:
HelloStart {
start {
print "Hey I am non-static 'start' method"
}
}
生成字節(jié)碼:
public class HelloStart {
public void start();
Code:
0: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #14 // String Hey I am non-static 'start' method
5: invokevirtual #19 // Method "Ljava/io/PrintStream;".println:(Ljava/lang/String;)V
8: return
//Constructor
public HelloStart();
Code:
0: aload_0 //get "this"
1: invokespecial #22 // Method java/lang/Object."<init>":()V - call super
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class HelloStart - create new object
3: dup //duplicate new object so that invokespecial does not consumes it
4: invokespecial #25 // Method "<init>":()V - call constructor
7: invokevirtual #27 // Method start:()V
10: return
}
與之對(duì)應(yīng) Java 類如下:
public class HelloStart {
public HelloStart() {
}
public static void main(String[] var0) {
(new HelloStart()).start();
}
public void start() {
System.out.println("Hey I am non-static \'start\' method");
}
}