Từ khóa out trong kotlin là gì


85

Tôi không thể hiểu và tôi không thể tìm thấy ý nghĩa của từ khóa out trong kotlin.

Bạn có thể kiểm tra ví dụ ở đây:

List<out T>

Nếu ai có thể giải thích ý nghĩa của điều này. Nó sẽ thực sự được đánh giá cao.

Câu trả lời:


58

Với chữ ký này:

List<out T>

bạn có thể làm được việc này:

val doubleList: List<Double> = listOf(1.0, 2.0)
val numberList: List<Number> = doubleList

có nghĩa là Thiệp phương sai :

khi một loại tham số T của một lớp C được khai báo ra , C <cơ sở> một cách an toàn có thể là một siêu kiểu của C <nguồn gốc> .

Điều này trái ngược với in , ví dụ

Comparable<in T>

bạn có thể làm được việc này:

fun foo(numberComparable: Comparable<Number>) {
  val doubleComparable: Comparable<Double> = numberComparable
  // ...
}

mà có nghĩa là Tcontravariant :

khi một loại tham số T của một lớp C được khai báo trong , C <nguồn gốc> một cách an toàn có thể là một siêu kiểu của C <cơ sở> .

Một cách khác để ghi nhớ nó:

Người tiêu dùng trong , Nhà sản xuất ra .

xem Kotlin Generics Variance

----------------- cập nhật vào ngày 4 tháng 1 năm 2019 -----------------

Đối với " Consumer in, Producer out ", chúng ta chỉ đọc từ Producer - phương thức gọi để nhận kết quả loại T; và chỉ ghi vào phương thức gọi của Người tiêu dùng bằng cách truyền vào tham số kiểu T.

Trong ví dụ List<out T>, rõ ràng là chúng ta có thể làm điều này:

val n1: Number = numberList[0]
val n2: Number = doubleList[0]

Vì vậy, nó là an toàn để cung cấp List<Double>khi List<Number>được mong đợi, do đó List<Number>là siêu loại List<Double>, nhưng không phải ngược lại.

Trong ví dụ cho Comparable<in T>:

val double: Double = 1.0
doubleComparable.compareTo(double)
numberComparable.compareTo(double)

Vì vậy, nó là an toàn để cung cấp Comparable<Number>khi Comparable<Double>được mong đợi, do đó Comparable<Double>là siêu loại Comparable<Number>, nhưng không phải ngược lại.


1
Tôi nghĩ rằng điểm quan trọng nhất đối với một List<out T>khai báo là outlàm cho nó không thể thay đổi (so với các bộ sưu tập có thể thay đổi, không có). Có thể hữu ích khi đề cập và nhấn mạnh điều đó trong câu trả lời. Truyền ngầm là hệ quả của điều này chứ không phải là điểm chính (vì người ta không thể ghi vào Danh sách <Số>, an toàn khi có nó như một tham chiếu đến Danh sách <Đôi>).
minsk

39
Xin lỗi, nhưng vẫn không thể hiểu được.
Akshay Taru

2
@minsk Mặc dù vậy, outmột phần không phải là thứ tạo nên sự Listbất biến. Bạn có thể dễ dàng tạo List<out T>giao diện của riêng mình có clear()phương thức vì nó không yêu cầu bất kỳ đối số nào.
Nick Lowery

đây là một trong những chủ đề mà bạn có thể muốn nhận được cho đến khi bạn thực sự cần nó ở đâu đó trong mã của mình.
lasec0203

109
List<out T> is like List<? extends T> in Java

List<in T> is like List<? super T> in Java

Ví dụ trong Kotlin, bạn có thể làm những việc như

 val value : List<Any> = listOf(1,2,3)
//since List signature is List<out T> in Kotlin

1
Câu trả lời này là ngược. List<out T>có nghĩa là bạn có thể làm val list: List<Number> = listOf<Int>()Intlà một loại dẫn xuất của Number. Java tương đương sẽ làList<? extends Number> list = new ArrayList<Integer>();
Nick Lowery

Điểm tốt, trong java nó là "? Expand T", không phải "? Super T". Đã sửa. Tôi chưa đề cập đến việc chúng tôi xác định Phương sai ở những nơi khác nhau để giữ cho câu trả lời đơn giản. Mọi người có thể đi đến tài liệu chính thức để biết tất cả các chi tiết. Vì vậy tôi không hiểu ý bạn là "câu trả lời là ngược".
DmitryBorodin

4

Tham khảo hướng dẫn sử dụng kotlin

Kiểu Kotlin List<out T>là một giao diện cung cấp các thao tác chỉ đọc như kích thước, lấy, v.v. Giống như trong Java, nó kế thừa từ Collection<T>và đến lượt nó kế thừa từ Iterable<T>. Các phương thức thay đổi danh sách được thêm vào bởi MutableList<T>giao diện. Mô hình này cũng giữ cho Set<out T>/MutableSet<T>Map<K, out V>/MutableMap<K, V>

Và điều này,

Trong Kotlin, có một cách để giải thích loại thứ này cho trình biên dịch. Đây được gọi là phương sai của trang web khai báo: chúng ta có thể chú thích tham số kiểu T của Nguồn để đảm bảo rằng tham số này chỉ được trả lại (được sản xuất) từ các thành viên của Source<T>và không bao giờ được tiêu thụ. Để làm điều này, chúng tôi cung cấp công cụ sửa đổi:

> abstract class Source<out T> {
>     abstract fun nextT(): T }
> 
> fun demo(strs: Source<String>) {
>     val objects: Source<Any> = strs // This is OK, since T is an out-parameter
>     // ... }

Nguyên tắc chung là: khi một tham số kiểu Tcủa một lớp Cđược khai báo, nó có thể chỉ xảy ra ở vị trí ngoài trong các thành viên của C, nhưng đổi lại C<Base>có thể an toàn là một kiểu siêu của C<Derived>.

Trong "các từ thông minh", họ nói rằng lớp Clà hiệp phương sai trong tham số T, hoặc đó Tlà một tham số kiểu hiệp phương sai. Bạn có thể coi C là nhà sản xuất của T, chứ KHÔNG phải là người tiêu dùng của T's. Công cụ sửa đổi out được gọi là chú thích phương sai và vì nó được cung cấp tại trang khai báo tham số kiểu, chúng ta nói về phương sai của trang khai báo. Điều này trái ngược với phương sai trang web sử dụng của Java trong đó các ký tự đại diện trong cách sử dụng kiểu làm cho các kiểu đồng biến.


3

Hãy nhớ như thế này:

inlà "cho vào " - bạn muốn đặt (viết) một cái gì đó vào đó (vì vậy đó là "người tiêu dùng")

outlà "for out put" - bạn muốn lấy (đọc) thứ gì đó từ nó (vì vậy nó là "nhà sản xuất")

Nếu bạn đến từ Java,

<in T>là đầu vào, vì vậy nó giống như <? super T>(người tiêu dùng)

<out T>là cho đầu ra, vì vậy nó giống như <? extends T>(nhà sản xuất)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.