進階型態對應探討


在Scala中,沒有傳統語言中所謂的運算子(Operator),所有+、-、*、/等都是 操 作方法。如果運算元是AnyVal的子類別,例如Int、Double等,在編譯為.class之後,直接對應至Java的+、-、*、/等運算,除此之外,所有的操作方法,基本上會編譯為$methodName這樣的名稱。

舉個例子來說,在 定義操作方法 中的例子:
class Complex(val re: Double, val im: Double) {
def + (that: Complex) = new Complex(re + that.re, im + that.im)
def - (that: Complex) = new Complex(re - that.re, im - that.im)
def * (that: Complex) = new Complex(re * that.re - im * that.im,
re * that.im + im * that.re)
def / (that: Complex) = {
val d = that.re * that.re + that.im * that.im
new Complex((re * that.re + im * that.im) / d,
(im * that.re - re * that.im) / d)
}
override def toString =
re + (if (im < 0) " - " + (-im) else " + " + im) + " i"

}

在編譯為.class後,其定義如下:
public class Complex extends java.lang.Object implements scala.ScalaObject{
    public Complex(double, double);
    public java.lang.String toString();
    public Complex $div(Complex);      // 除 /
    public Complex $times(Complex);    // 乘 *
    public Complex $minus(Complex);    // 減 -
    public Complex $plus(Complex);     // 加 +
    public double im();
    public double re();
    public int $tag()       throws java.rmi.RemoteException;
}

所以,如果你真的要呼叫那些在Scala中的運算子,以下是個例子:
public class Main {
public static void main(String[] args) {
Complex c1 = new Complex(3, 2);
Complex c2 = new Complex(2, 3);
Complex rc = c1.$plus(c2);
System.out.println(rc);
}
}

以下為Scala運算子編譯為.class後的名稱對照:
ScalaJava
+$plus
-$minus
*$times
/$div
%$percent
&$amp
|$bar
^$up
!$bang
~$tilde
?$qmark
#$hash
:$colon
\$bslash
@$at
>$greater
=$eq
<$less

如果是::這樣連續兩個符號,則編譯後會變成$colon$colon這樣的名稱。

在Scala中,一級函式(First-class function)其實都是FunctionN的實例,其中N為數字,代表參數個數。例如:
trait Fun {
def f1(x: () => String)
def f2(x: (String) => String)
def f3(x: (String, String) => String)
def f3(x: (String, String, String) => String)
}

在編譯為.class後,其定義為:
import scala.Function0;
import scala.Function1;
import scala.Function2;
import scala.Function3;

public interface Fun {
  public void f3(Function3<String, String, String, String> paramFunction3);
  public void f3(Function2<String, String, String> paramFunction2);
  public void f2(Function1<String, String> paramFunction1);
  public void f1(Function0<String> paramFunction0);
}

在< >的泛型宣告中,最後一個是代表Scala中的傳回值型態,前幾個則是參數型態。實際想在Java中使用Scala所宣告的函式物件會更為複雜(而失去這麼作的意義),因為FunctionN在Scala中是特徵(Trait),以Function1為例:
package scala
trait Function1[-T1, +R] extends AnyRef { self =>
def apply(v1:T1): R
override def toString() = "<function>"
def compose[A](g: A => T1): A => R = { x => apply(g(x)) }
def andThen[A](g: R => A): T1 => A = { x => g(apply(x)) }
}

編譯為Java所產生的.class之一Function1.class為介面(interface)定義:
package scala;

public abstract interface Function1<T1, R> extends ScalaObject {
  public abstract <A> Function1<T1, A> andThen(Function1<R, A> paramFunction1);
  public abstract <A> Function1<A, R> compose(Function1<A, T1> paramFunction1);
  public abstract String toString();
  public abstract R apply(T1 paramT1);
}

函式的執行本體最主要是定義在apply()方法之中,所以就算你打算忽略其它的方法,也得這麼實作:
Function1<String, String> f = new Function1<String, String>() {
    public <A> Function1<String, A> andThen(Function1<String, A> f) {
        return null;
    }
    public <A> Function1<A, String> compose(Function1<A, String> f) {
        return null;
    }
    public int $tag() throws java.rmi.RemoteException {
        return 0;
    }
    public String toString() {
        return "<function>";
    }
    public String apply(String paramT1) {
        return paramT1 + "...Orz";
    }
};

再來談到 作為共用實作的特徵,例如:
trait Ordered {
def compare(that: Any): Int
def < (that: Any) = compare(that) < 0
def <=(that: Any) = (this < that) || (this == that)
def > (that: Any) = !(this <= that)
def >=(that: Any) = !(this < that)
}

在編譯為Java之後,會產生Ordered.class與Ordered$class.class,其中Ordered以介面定義了必須實作的方法,而Ordered$class為實作的部份:
// Ordered.class
import scala.ScalaObject;

public interface Ordered extends ScalaObject {

    public abstract boolean $greater$eq(Object obj);
    public abstract boolean $greater(Object obj);
    public abstract boolean $less$eq(Object obj);
    public abstract boolean $less(Object obj);
    public abstract int compare(Object obj);
}

// Ordered$class.class
import scala.runtime.BoxesRunTime;


public abstract class Ordered$class {
    public static void $init$(Ordered ordered) { }

    public static boolean $greater$eq(Ordered $this, Object that) {
        return !$this.$less(that);
    }

    public static boolean $greater(Ordered $this, Object that) {
        return !$this.$less$eq(that);
    }

    public static boolean $less$eq(Ordered $this, Object that) {
        return $this.$less(that) || BoxesRunTime.equals($this, that);
    }

    public static boolean $less(Ordered $this, Object that) {
        return $this.compare(that) < 0;
    }
}

也就是說,如果你真的要在Java中實現Scala中的這個類別:
class Ball(val radius: Int) extends Ordered {
def compare(a: Any) = a match {
...
}
...
}

那麼你必須類似這麼作:
public class Ball implements Ordered, ScalaObject {
    ....
    public int compare(Object a) {
        ...
    }
    public boolean $greater$eq(Object x$1) {
        return Ordered.class.$greater$eq(this, x$1);
    }
    public boolean $greater(Object x$1) {
        return Ordered.class.$greater(this, x$1);
    }
    public boolean $less$eq(Object x$1) {
        return Ordered.class.$less$eq(this, x$1);
    }
    public boolean $less(Object x$1) {
        return Ordered.class.$less(this, x$1);
    }
}

再來談談 結構型態(Structural typing),例如:
class XD {
def doQuack(d: {def quack: String}) {
println(d.quack)
}
}

在編譯為.class後,其定義為:
public class XD extends java.lang.Object implements scala.ScalaObject{
    public XD();
    public void doQuack(java.lang.Object);
    public int $tag()       throws java.rmi.RemoteException;
    public static java.lang.reflect.Method reflMethod$Method1(java.lang.Class)
}

再明顯不過,doQuack()的參數型態為Object,而在內部,使用了反射(Reflection)來呼叫指定的方法。