2011-12-16 43 views
15

Nếu chức năng chấp nhận kiểu cấu trúc, nó có thể được định nghĩa là:Tại sao scala sử dụng phản xạ để gọi phương thức trên kiểu cấu trúc?

def doTheThings(duck: { def walk; def quack }) { duck.quack } 

hoặc

type DuckType = { def walk; def quack } 
def doTheThings(duck: DuckType) { duck.quack } 

Sau đó, bạn có thể sử dụng chức năng theo cách sau:

class Dog { 
    def walk { println("Dog walk") } 
    def quack { println("Dog quacks") } 
} 

def main(args: Array[String]) { 
    doTheThings(new Dog); 
} 

Nếu bạn dịch ngược (đối với Java) các lớp được tạo bởi scalac cho ví dụ của tôi, bạn có thể thấy đối số của doTheThings là loại Object và im plementation sử dụng sự phản chiếu để gọi các phương thức trên đối số (tức là duck.quack)

Câu hỏi của tôi là lý do phản ánh? Không phải là nó chỉ có thể sử dụng vô danh và invokevirtual thay vì phản ánh?

Dưới đây là cách để dịch (thực hiện) các loại cấu trúc gọi ví dụ của tôi (cú pháp Java, nhưng điểm mấu chốt là các bytecode):

class DuckyDogTest { 
    interface DuckType { 
    void walk(); 
    void quack(); 
    } 

    static void doTheThing(DuckType d) { 
    d.quack(); 
    } 

    static class Dog { 
    public void walk() { System.out.println("Dog walk"); } 
    public void quack() { System.out.println("Dog quack"); } 
    } 

    public static void main(String[] args) { 
    final Dog d = new Dog(); 
    doTheThing(new DuckType() { 
     public final void walk() { d.walk(); } 
     public final void quack() { d.quack();} 
    }); 
    } 
} 

Trả lời

13

Hãy xem xét một đề xuất đơn giản:

type T = { def quack(): Unit; def walk(): Unit } 
def f(a: T, b: T) = 
    if (a eq b) println("They are the same duck!") 
    else  println("Different ducks") 

f(x, x) // x is a duck 

Nó sẽ in Different ducks theo đề xuất của bạn. Bạn có thể tinh chỉnh thêm nó, nhưng bạn không thể giữ nguyên bình đẳng tham chiếu bằng cách sử dụng một proxy.

Một giải pháp có thể là sử dụng mẫu kiểu lớp, nhưng điều đó sẽ yêu cầu chuyển một tham số khác (ngay cả khi ẩn). Tuy nhiên, nó nhanh hơn. Nhưng đó là chủ yếu là do độ mỏng của tốc độ phản chiếu của Java. Hy vọng rằng, phương pháp xử lý sẽ nhận được xung quanh vấn đề tốc độ. Thật không may, Scala không được lên kế hoạch từ bỏ trên Java 5, 6 và 7 (mà không có xử lý phương pháp) trong một thời gian ...

+0

Tôi không nhận được câu cuối cùng, bạn có thể, vui lòng giải thích? –

+0

@ om-nom-nom 'invokevirtual' không có mặt trên JVM 1.5 và 1.6, vì vậy Scala không thể dựa vào nó. Scala 2.10 sẽ thực sự không dùng JVM 1.5, nhưng vẫn còn một thời gian trước khi Scala có thể tận dụng những thứ chỉ có trên JVM 1.7. –

+0

@Daniel C. Sobral: Tôi đoán bạn có nghĩa là 'invokedynamic' thay vì' invokevirtual' trong bình luận cuối cùng của bạn –

10

Ngoài đối tượng proxy của bạn thực hiện các phương pháp về kiểu cấu trúc, nó cũng sẽ cần phải có việc triển khai thông qua thích hợp của tất cả các phương thức trên Mọi (bằng, hashCode, toString, isInstanceOf, asInstanceOf) và AnyRef (getClass, chờ, thông báo, thông báoAll và đồng bộ). Trong khi một số trong số này sẽ được đơn giản, một số sẽ gần như không thể có được quyền. Đặc biệt, tất cả các phương thức được liệt kê là "cuối cùng" trên AnyRef (đối với tính tương thích và bảo mật của Java) và do đó đối tượng proxy của bạn không thể thực hiện đúng.

+1

@Daniel C.Sobral và Dave Griffith: Cả hai câu trả lời của bạn đều được chấp nhận. Vì vậy, tôi đã phải ném một đồng xu để chính thức chấp nhận một. –

Các vấn đề liên quan