2016-11-27 11 views
6

Tôi đã tự hỏi liệu có một lý do chính đáng để sử dụng kiểu con là tham số kiểu hàm? Hãy xem xét ví dụ sau:Có lý do nào để sử dụng loại phụ như thông số kiểu trong Scala không?

scala> trait Animal { def sound: String } 
defined trait Animal 

scala> def f1[T <: Animal](a: T) = a.sound 
f1: [T <: Animal](a: T)String 

scala> def f2(a: Animal) = a.sound 
f2: (a: Animal)String 

f1 một số ưu điểm so với f2?

+2

Nếu 'f1' cần thiết để trả về một' T' hoặc một số loại mà tham chiếu 'T ', sau đó chắc chắn. –

Trả lời

5

Tôi tin rằng không có lợi thế trong ví dụ của bạn. Thông số loại thường được sử dụng để kết nối các phần khác nhau của mã, nhưng thông tin về T bị mất hiệu quả vì nó không khớp với bất kỳ thứ gì. Hãy xem xét một ví dụ:

def f1[T <: Animal](a: T) = (a.sound, a) 

def f2(a: Animal) = (a.sound, a) 

Bây giờ mọi thứ đã khác như T được chuyển cho một kiểu trả về:

class Dog extends Animal { def sound = "bow-wow" } 

f1(new Dog) 
//> (String, Dog) = (bow-wow,[email protected]) 

f2(new Dog) 
//> (String, Animal) = (bow-wow,[email protected]) 

Trong trường hợp này bạn có thể nghĩ f1 như của một mẫu được "khởi tạo" tại biên dịch thời gian và hiệu quả tạo ra một phương thức cụ thể dựa trên các kiểu tham số biên dịch thời gian. Vì vậy, nếu bạn muốn sử dụng f1(new Dog) trong đó (String, Dog) là bắt buộc, nó sẽ biên dịch, trong khi f2(new Dog) thì không.

2

Cả hai chức năng f1f2 đều rất giống nhau. Nếu bạn xuất bytecode, bạn sẽ xem tối đa trong các hằng số hồ bơi mà họ được biểu hiện như:

#71 = Methodref   #12.#70  // Main$.f2:(LMain$Animal;)Ljava/lang/String; 
#74 = Methodref   #12.#73  // Main$.f1:(LMain$Animal;)Ljava/lang/String; 

Theo như các bytecode là có liên quan, họ là những chức năng mà phải mất một Animal như một tham số và trả lại String.

Một trường hợp điều này trở nên thú vị hơn là khi bạn muốn trả lại một số T cụ thể (trong đó T <: Animal). Hãy ghi nhớ, các bytecode vẫn sẽ giống nhau, nhưng tại thời gian biên dịch này cho biết thêm ý nghĩa và sức mạnh để tham số T loại:

Hãy tưởng tượng bạn có:

def f1[T <: Animal](a: T): T = a // silly, I know 
def f2(a: Animal): Animal = a 

Và bạn thử điều này:

val s: Dog = f1(new Dog()) 
val t: Dog = f2(new Dog()) // NOPE 
val u: Dog = f2(new Dog()).asInstanceOf[Dog] // awkward 

Dòng thứ hai đó sẽ không biên dịch mà không truyền, hy sinh việc kiểm tra kiểu biên dịch.

+0

Điều thú vị là 'f1' và' f2' của bạn sẽ được biên dịch thành cùng một mã. Các thông số loại sẽ bị xóa. –

+0

Có. Sau khi biên dịch, bytecode sẽ giống nhau. Loại xóa sẽ làm cho nó xuất hiện như thể 'f1' trả về' Động vật'. Điểm tôi đang cố gắng để làm cho có sự hữu ích _at_ biên dịch-thời gian khi các thông số loại "vấn đề". – drhr

+0

Tôi đã đề cập nó bởi vì bạn đã bắt đầu (tôi đã làm nó quá ban đầu) từ "nó biên dịch sang cùng một bytecode". –

0

Trong Scala và JVM họ đã sau cai trị chức năng gõ

S1 -> S2 is subtype of T1 -> T2

if and only if

S1 is subtype of T1 and T2 is subtype of S2

Trong ví dụ của bạn,

def f1[T <: Animal](a: T): String // T <: Animal -> String 
    def f2(a: Animal): String // Animal -> String 

Bằng quy tắc gõ chức năng, f1 là subtype của f2

Trong kết luậnf1f2 không có differenc e trong trường hợp sử dụng thực

Vui lòng tham khảo, liên kết sau

https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Function_types

+0

Cả hai 'f1' lẫn' f2' đều không có các kiểu như chúng không hoạt động, chúng là các phương thức. Ngoài ra eta-mở rộng có thể cung cấp cho bạn kết quả khác nhau tùy thuộc vào những gì bạn mong đợi. Hãy thử 'f1 _' trong REPL, bạn sẽ ngạc nhiên. –

1

Xét ví dụ của bạn, nơi f1f2 có cùng công suất, gõ f2 chức năng có lợi thế trước f1 nếu bạn muốn quá tải của bạn phương pháp.

Vì vậy, đối f1 nó sẽ cho một lỗi:

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

class Zoo { 
    def f1[T <: Animal](a: T) = a.sound 
    def f1[T <: Dog](a: T) = "Dog says " + a.sound 
} 

// Exiting paste mode, now interpreting. 

<console>:18: error: method f1 is defined twice 
    conflicting symbols both originated in file '<console>' 
     def f1[T <: Dog](a: T) = "Dog says " + a.sound 
      ^

trong khi với f2 nó hoạt động:

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

class Zoo { 
    def f2(a: Animal) = a.sound 
    def f2(a: Dog) = "Dog says " + a.sound 
} 

// Exiting paste mode, now interpreting. 

defined class Zoo 
Các vấn đề liên quan