Gọi theo tên: => Loại
Các => Type
ký hiệu viết tắt của cuộc gọi-by-tên, đó là một trong những rất nhiều cách thông số có thể được thông qua. Nếu bạn không quen thuộc với họ, tôi khuyên bạn nên dành chút thời gian để đọc bài viết trên wikipedia đó, mặc dù ngày nay nó chủ yếu là gọi theo giá trị và gọi theo tham chiếu.
Điều đó có nghĩa là những gì được thông qua được thay thế cho tên giá trị bên trong hàm. Ví dụ: lấy chức năng này:
def f(x: => Int) = x * x
Nếu tôi gọi nó như thế này
var y = 0
f { y += 1; y }
Sau đó, mã sẽ thực thi như thế này
{ y += 1; y } * { y += 1; y }
Mặc dù điều đó làm tăng quan điểm về những gì sẽ xảy ra nếu có xung đột tên định danh. Trong cách gọi tên truyền thống, một cơ chế được gọi là thay thế tránh bắt giữ diễn ra để tránh xung đột tên. Tuy nhiên, trong Scala, điều này được thực hiện theo một cách khác với cùng kết quả - tên định danh bên trong tham số không thể tham chiếu hoặc định danh bóng trong hàm được gọi.
Có một số điểm khác liên quan đến tên gọi mà tôi sẽ nói đến sau khi giải thích hai cái còn lại.
Hàm 0-arity: () => Loại
Cú pháp () => Type
là viết tắt của loại a Function0
. Đó là, một hàm không có tham số và trả về một cái gì đó. Điều này tương đương với, gọi, gọi phương thứcsize()
- nó không có tham số và trả về một số.
Tuy nhiên, điều thú vị là cú pháp này rất giống với cú pháp cho một hàm ẩn danh theo nghĩa đen , là nguyên nhân gây ra một số nhầm lẫn. Ví dụ,
() => println("I'm an anonymous function")
là một hàm ẩn danh theo nghĩa đen của arity 0, có kiểu là
() => Unit
Vì vậy, chúng tôi có thể viết:
val f: () => Unit = () => println("I'm an anonymous function")
Tuy nhiên, điều quan trọng là không nhầm lẫn loại với giá trị, tuy nhiên.
Đơn vị => Loại
Đây thực sự chỉ là một Function1
, có tham số đầu tiên là loại Unit
. Những cách khác để viết nó sẽ là (Unit) => Type
hoặc Function1[Unit, Type]
. Vấn đề là ... điều này khó có thể trở thành điều người ta muốn. Các Unit
mục đích chính của loại được chỉ ra một giá trị không được quan tâm, vì vậy không có ý nghĩa để nhận được giá trị đó.
Xem xét, ví dụ,
def f(x: Unit) = ...
Người ta có thể làm gì với x
? Nó chỉ có thể có một giá trị duy nhất, vì vậy người ta không cần phải nhận nó. Một cách sử dụng có thể là các hàm chuỗi trở lại Unit
:
val f = (x: Unit) => println("I'm f")
val g = (x: Unit) => println("I'm g")
val h = f andThen g
Bởi vì andThen
chỉ được xác định Function1
và các chức năng mà chúng tôi đang kết nối sẽ quay trở lại Unit
, chúng tôi phải xác định chúng là loại Function1[Unit, Unit]
để có thể xâu chuỗi chúng.
Nguồn gây nhầm lẫn
Nguồn gây nhầm lẫn đầu tiên là suy nghĩ về sự giống nhau giữa loại và nghĩa đen tồn tại đối với các hàm 0-arity cũng tồn tại đối với tên gọi. Nói cách khác, nghĩ rằng, bởi vì
() => { println("Hi!") }
là một nghĩa đen cho () => Unit
, sau đó
{ println("Hi!") }
sẽ là một nghĩa đen cho => Unit
. Không phải vậy. Đó là một khối mã , không phải là một nghĩa đen.
Một nguồn gây nhầm lẫn khác là giá trịUnit
của loại được viết , trông giống như một danh sách tham số 0-arity (nhưng không phải vậy).()
case class Scheduled(time: Int)(callback: => Unit)
. Điều này hoạt động vì danh sách tham số phụ không được hiển thị công khai và cũng không được bao gồm trong các phương thức được tạoequals
/hashCode
.