2009-10-17 6 views
9

Trong Scala, bạn có thể quá tải một phương thức bằng cách sử dụng các phương thức chia sẻ một tên chung, nhưng có một số kiểu khác nhau hoặc các kiểu tham số khác nhau. Tôi đã tự hỏi tại sao điều này cũng không được mở rộng đến kiểu trả về của một phương thức? Hãy xem xét mã sau đây:Tại sao quá tải phương thức không được xác định cho các loại trả về khác nhau?

class C { 
    def m: Int = 42 
    def m: String = "forty two" 
} 

val c = new C 
val i: Int = C.m 
val s: String = C.m 

Có lý do nào khiến việc này không hoạt động không?

Cảm ơn bạn,

Vincent.

+0

Xem thêm Java cụ thể http://stackoverflow.com/questions/2744511/java-why-no-return-type-based-method-overloading?lq=1 – nawfal

+0

và nói chung http : //stackoverflow.com/questions/442026/function-overloading-by-return-type – nawfal

Trả lời

5

Tất nhiên, bạn có thể quá tải cho các phương thức khác nhau theo loại trả lại, không chỉ cho các phương thức khác nhau chỉ theo loại trả về. Ví dụ, đây là tốt:

def foo(s: String) : String = s + "Hello" 
def foo(i: Int) : Int = i + 1 

đó sang một bên, câu trả lời cho câu hỏi của bạn là rõ ràng rằng đó là một quyết định thiết kế: kiểu trả về là một phần của phương pháp chữ ký như bất cứ ai đã trải qua một AbstractMethodError thể cho bạn biết .

xem xét tuy nhiên cách cho phép quá tải như vậy có thể làm việc song song với phụ gõ:

class A { 
    def foo: Int = 1 
} 
val a: A = //...lookup an A 
val b = a.foo 

này là hoàn toàn hợp lệ mã của khóa học và javac duy nhất sẽ giải quyết các lời gọi phương thức. Nhưng điều gì sẽ xảy ra nếu tôi phân loại A như sau:

class B extends A { 
    def foo: String = "Hello" 
} 

Điều này làm cho độ phân giải của mã gốc được gọi là bị hỏng. Điều gì nên b được? Tôi đã phân tích một cách hợp lý một số mã hiện có bằng cách phân lớp một số lớp hiện có, mặc dù tôi đã không thay đổi mã đó hoặc lớp đó.

+2

loại tĩnh của a là A và A chỉ có một phương thức được gọi là foo (phương thức trả về Int), vậy thì sự mơ hồ ở đâu? –

2

Tôi chưa bao giờ sử dụng scala, vì vậy ai đó đánh vào đầu tôi nếu tôi sai ở đây, nhưng đây là việc của tôi.

Giả sử bạn có hai phương thức có chữ ký chỉ khác nhau theo loại trả về.

Nếu bạn đang gọi phương thức đó, trình biên dịch (thông dịch viên) biết phương thức nào bạn thực sự muốn gọi?

Tôi chắc chắn trong một số trường hợp, nó có thể xác định được điều đó, nhưng điều gì sẽ xảy ra nếu, ví dụ, một trong các kiểu trả về của bạn là một lớp con của lớp kia? Nó không phải lúc nào cũng dễ dàng.

Java không cho phép quá tải các loại trả về và vì scala được xây dựng trên Java JVM, có lẽ đó chỉ là giới hạn java.

(Chỉnh sửa) Lưu ý rằng trả về Covariant là một vấn đề khác. Khi ghi đè một phương thức, bạn có thể chọn trả về một lớp con của lớp mà bạn đang định quay trở lại, nhưng không thể chọn một lớp không liên quan để trả về.

1

Để phân biệt giữa chức năng khác nhau với cùng tên và loại đối số, nhưng các loại trả về khác nhau, một số cú pháp là bắt buộc hoặc phân tích trang web của một biểu thức.

Scala là ngôn ngữ định hướng biểu thức (mỗi câu lệnh là một biểu thức). Nói chung ngôn ngữ định hướng biểu thức thích có ngữ nghĩa biểu thức chỉ phụ thuộc vào việc đánh giá phạm vi xảy ra, không phải điều gì xảy ra với kết quả, do đó, đối với biểu thức foo() trong i_take_an_int(foo())i_take_any_type (foo())foo() như một tuyên bố tất cả gọi cùng một phiên bản foo() . Ngoài ra còn có vấn đề thêm quá tải theo kiểu trả về cho ngôn ngữ với suy luận kiểu sẽ làm cho mã hoàn toàn không thể hiểu được - bạn phải giữ một số lượng đáng kinh ngạc của hệ thống để dự đoán điều gì sẽ xảy ra khi mã được thực hiện.

6

Lý do chính là vấn đề phức tạp: với cách tiếp cận trình biên dịch "bình thường", bạn vào trong ra ngoài (từ biểu thức bên trong đến phạm vi bên ngoài), xây dựng bước nhị phân của bạn theo từng bước; nếu bạn thêm sự khác biệt kiểu chỉ trả về, bạn cần phải thay đổi cách tiếp cận ngược, làm tăng đáng kể thời gian biên dịch, độ phức tạp của trình biên dịch (= lỗi!).

Ngoài ra, nếu bạn trả lại loại phụ hoặc loại có thể được chuyển đổi tự động sang loại phụ khác, bạn nên chọn phương pháp nào? Bạn sẽ đưa ra các lỗi mơ hồ cho mã hoàn toàn hợp lệ.

Không đáng để khắc phục sự cố.

Tất cả trong tất cả, bạn có thể dễ dàng cấu trúc lại mã của mình để tránh tình trạng quá tải chỉ trả về, ví dụ bằng cách thêm thông số giả của loại bạn muốn trả về.

7

Thực ra, bạn có thể làm cho nó hoạt động bằng sự kỳ diệu của 'ngầm'. Như sau:

scala> case class Result(i: Int,s: String) 

scala> class C { 
    |  def m: Result = Result(42,"forty two") 
    | } 

scala> implicit def res2int(res: Result) = res.i 

scala> implicit def res2str(res: Result) = res.s 

scala> val c = new C 

scala> val i: Int = c.m 

i: Int = 42 


scala> val s: String = c.m 

s: String = forty two 

scala> 
+0

Chính xác những gì tôi đang tìm kiếm, cảm ơn – MikeB

1

Tất cả câu trả lời cho biết JVM không cho phép điều này sai trái. Bạn có thể quá tải dựa trên loại trả lại. Đáng ngạc nhiên, JVM không cho phép điều này; đó là các trình biên dịch cho các ngôn ngữ chạy trên JVM không cho phép điều này. Nhưng có nhiều cách để vượt qua giới hạn trình biên dịch trong Scala.

Ví dụ, hãy xem xét các đoạn mã sau đây:

object Overload{ 
    def foo(xs: String*) = "foo" 
    def foo(xs: Int*) = "bar" 
} 

này sẽ ném một lỗi biên dịch (Bởi vì varargs, chỉ định bởi các * sau kiểu lập luận, gõ xóa để Seq):

Error:(217, 11) double definition: 
def foo(xs: String*): String at line 216 and 
def foo(xs: Any*): String at line 217 
have same type after erasure: (xs: Seq)String 
     def foo(xs: Any*) = "bar"; 

Tuy nhiên, nếu bạn thay đổi giá trị của số thứ hai foo thành 3 thay vì bar (cách đó thay đổi loại trả về từ String thành Int) như sau:

object Overload{ 
    def foo(xs: String*) = "foo" 
    def foo(xs: Int*) = 3 
} 

... bạn sẽ không nhận được một lỗi biên dịch.

Vì vậy, bạn có thể làm một cái gì đó như thế này:

val x: String = Overload.foo() 
val y: Int = Overload.foo() 

println(x) 
println(y) 

Và nó sẽ in ra:

3 
foo 

Tuy nhiên, sự báo trước cho phương pháp này là có thêm varargs là cuối cùng (hoặc chỉ) đối số cho các hàm bị quá tải, mỗi hàm có kiểu riêng biệt của chúng.

Nguồn: http://www.drmaciver.com/2008/08/a-curious-fact-about-overloading-in-scala/