2010-09-10 33 views
11

Nói rằng tôi có:Việc mở rộng một lớp trong scala với tham số hàm tạo thêm vals (fields) vào lớp không?

class A(val foo: String) 

class B(foo: String) extends A(foo) 
class C(val foo: String) extends A(foo) 
class D(override val foo: String) extends A(foo) 
class E(bar: String) extends A(bar) 

Tôi quan tâm đến bao nhiêu trường hợp ký ức của mỗi người trong số các lớp này mất. Các cá thể của lớp A sẽ có một biến thành viên: foo.

Còn lớp B, C, D và E thì sao? Mỗi người sẽ có bao nhiêu biến thành viên? Tôi nghi ngờ E sẽ có hai (E.bar, A.foo), tôi mong đợi D sẽ có một (A.foo), nhưng tôi tự hỏi về B và C, họ có thể có hai? (B.foo, A.foo)?

+0

Chỉ cần lưu ý nhanh rằng cả hai phương thức var và val tạo trên các tệp lớp được tạo bởi Scala. Việc tạo ra các trường có phần mờ nhạt đối với người dùng, do đó bạn không thể dựa vào bất kỳ trường cụ thể nào được tạo ra hoặc tên của một trường như vậy. – jsuereth

+0

cảm ơn thông tin jseureth –

Trả lời

14

Tất cả các ví dụ biên dịch (A, B, D, E) lấy chính xác cùng một lượng dung lượng lưu trữ. Thực tế, ngay cả

class F(val bar: String) extends A(bar) 

sẽ có dữ liệu được lưu trữ trong một trường - nó chỉ nhận được phương thức truy cập phụ cho cùng một trường. Tuy nhiên, nếu bạn

class G(var bar: String) extends A(bar) 

thì trường mới sẽ được tạo.

Bạn có thể kiểm tra tất cả các điều này bằng cách soạn thảo ví dụ của bạn ở trên và nhìn vào bytecode từ javap -c Classname (chú ý putfield tại 2: trong constructor của A):

public class Sizes$A extends java.lang.Object implements scala.ScalaObject{ 
public java.lang.String foo(); 
    Code: 
    0: aload_0 
    1: getfield #11; //Field foo:Ljava/lang/String; 
    4: areturn 

public Sizes$A(java.lang.String); 
    Code: 
    0: aload_0 
    1: aload_1 
    2: putfield #11; //Field foo:Ljava/lang/String; 
    5: aload_0 
    6: invokespecial #18; //Method java/lang/Object."<init>":()V 
    9: return  
} 

(Và việc thiếu một putfield thêm trong F ...)

public Sizes$F(java.lang.String); 
    Code: 
    0: aload_0 
    1: aload_1 
    2: invokespecial #15; //Method Sizes$A."<init>":(Ljava/lang/String;)V 
    5: return 

(Và sự hiện diện của một lần nữa trong G ...)

public Sizes$G(java.lang.String); 
    Code: 
    0: aload_0 
    1: aload_1 
    2: putfield #11; //Field bar:Ljava/lang/String; 
    5: aload_0 
    6: aload_1 
    7: invokespecial #18; //Method Sizes$A."<init>":(Ljava/lang/String;)V 
    10: return 
7

Bạn lớp C sẽ yêu cầu một từ khóa override trên val foo để biên dịch, hiển thị nó giống hệt với D. Nó sẽ lưu trữ bản sao riêng của foo. Bạn lớp B không thêm bộ nhớ trừ khi một nơi nào đó bên ngoài phần thân của hàm tạo của nó có tham chiếu đến foo. Điều đó sẽ buộc một trường ẩn được tạo ra để giữ tham số hàm tạo. Phần tử của hàm tạo là tất cả mã trong định nghĩa lớp và bên ngoài bất kỳ thân phương thức nào.

Phụ Lục:

package storage 

    class A(val foo: String) 

    class B(   foo: String) extends A(foo) 
// class C(  val foo: String) extends A(foo) 
    class D(override val foo: String) extends A(foo) 
    class E(   bar: String) extends A(bar) 
    class F(   bar: String) extends A(bar) { def barbar: String = bar } 

Tôi lúng túng của thành viên này:

% javap -private storage.F 
Compiled from "Storage.scala" 
public class storage.F extends storage.A implements scala.ScalaObject{ 
    public java.lang.String barbar(); 
    public storage.F(java.lang.String); 
} 

gì là phương pháp barbar sử dụng để có được giá trị trả về của nó?

+0

'B' cũng sẽ không thêm trường cho' foo' trong trường hợp bạn đã truy cập trường đó bên ngoài phần thân của hàm tạo nhưng sử dụng trình truy cập của cha mẹ. – Moritz

+0

Cảm ơn thông tin Randall. Chỉ cần làm rõ: bạn có nói rằng các trường hợp của D sẽ có hai bản sao của foo? –

+0

@Alex Black: Có. Lệnh 'javap' là tốt cho việc tiết lộ các chi tiết này, nhưng lưu ý rằng bạn cần sử dụng tùy chọn' -private' để xem tất cả các trường riêng trả về các phương thức accessor (và mutator) mà trình biên dịch sử dụng để làm trung gian truy cập vào lĩnh vực. –

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