首页 安卓& Kotlin Books Kotlin学徒

18
仿制 由Ellen Shapiro撰写

在编程中,集中您的代码是保存自己头痛并防止错误的最大方法之一。这样,当你在多个地方做同样的事情时,只有一个地方实际上已经完成了一个地方,只有一个地方可能会破裂。

Kotlin为此的一个非常有用的特征被称为 仿制。通用编程的一般概念是您并不一定需要确切地了解对象是什么 - 或与您正在使用的主对象关联的对象 - 以便在其周围执行操作。这允许您以真正强大的方式组合和简化功能。

标准图书馆通用类型的解剖

在泛型开始时,它有助于查看Kotlin标准库中包含的主要通用类型。这样,您可以看出语言本身如何使用此功能,并了解您如何自己使用它的想法。

清单

你可能已经注意到了 列表 objects that you sometimes need to declare them with the type of item you expect in the list in angle brackets, such as 列表<String>, or 列表<Int>.

interface List<out E> : Collection<E>
val names: List<String> = listOf("Bob", "Carol", "Ted", "Alice")
println("Names: $names")
val firstName = names.first()
fun <T> List<T>.first(): T
println(firstName)
val firstname:string
Bap heypkkaka:hjnavg

Names: [Bob, Carol, Ted, Alice]
Bob
val names = listOf("Bob", "Carol", "Ted", "Alice")
val firstInt: Int = names.first()
类型不匹配。必填:int,找到:字符串
Jryi Wuhxehvf。 HIWEOMAD:ODJ,Raunf:CMPAWX

val things = mutableListOf(1, 2)
things.add("Steve")
println("Things: $things")
类型不匹配。必填:int,找到:字符串
MBBA QAHPUMSB。 vemuuzaq:arf,yeirm:ywcixr

val things: MutableList<Any> = mutableListOf(1, 2)
val things = mutableListOf<Any>(1, 2)
Things: [1, 2, Steve]

地图

地图比列表更复杂,因为他们为您提供使用而不是一个但是 generic types.

interface Map<K, out V>
val map = mapOf(
  Pair("one", 1),
  Pair("二", "II"),
  Pair("three", 3.0f)
)
val one = map.get(1)
类型推断失败。类型参数K的值应在输入类型被提及。尽量明确指定。
Htdu iwhasotlu kuadoh。 ZMO sidee是ZFI zjco tolebicaw Q fsuunc做我cekgoejog uvmos qdxef。 LFT tkisidq ejjduweyvf。

val one = map[1]
类型推断失败。类型参数K的值应在输入类型被提及。尽量明确指定。
Zrva ofkanukqe jiajab。 Zze befee LQE yqsa haticuyis V ddaopq GE qukboiget招聘ixyax bdlep。 NJZ LO wkudakl AC ebhsunecnc。

val valuesForKeysWithE = map.keys
    .filter { it.contains("e") }
    .map { "Value for $it: ${map[it]}" }
println("Values for keys with E: $valuesForKeysWithE")
Values for keys with E: [Value for one: 1, Value for three: 3.0]

具有通用约束的类型的扩展功能

You’ve been printing out a lot of 列表 objects so far, and you may have noticed they don’t look all that good in the console: They’re always on a single line so it’s difficult to tell what’s actually contained within them or how many objects there are. Say you wanted to print every single line on its own line so that printing a list would look more like this:

- First Item
- Second item
- Third Item
fun List<String>.toBulletedList(): String {
  val separator = "\n - "
  return this.map { "$it" }.joinToString(separator, prefix = separator, postfix = "\n")
}
println("Names: ${names.toBulletedList()}")
println("Values for keys with E: ${valuesForKeysWithE.toBulletedList()}")
Names:
 - Bob
 - Carol
 - Ted
 - Alice

Bob
Things: [1, 2, Steve]
Values for keys with E:
 - Value for one: 1
 - Value for three: 3.0
println("Things: ${things.toBulletedList()}")
未解决的参考。
AsnoreVhog Zivanijno。

fun List<Any>.toBulletedList(): String {
}
Clatform声明冲突块:下面的声明伴随着同样的JVM Sigerature
Qvihpoyq Wus Axiferop Xhohk:XJO Lahhahhulz Jobbelulz Jabiagulj Jani Sla Hano LyvWurbage

fun List<T>.toBulletedList(): String
未解决方案参考:T
ejjurawfoh bipayafte:D

fun <T> List<T>.toBulletedList(): String
Things:
 - 1
 - 2
 - Steve

创建自己的通用约束

使用泛型的另一种强大的方法是为类,函数和变量提供通用约束 创建。这样一来,就可以创造的东西,可以让你在一个集中的方式操作,但传递要用于该约束随便你!

// 1
class Mover<T>(
    // 2
    thingsToMove: List<T>,
    val truckHeightInInches: Int = (12 * 12)
) {

  // 3
  private var thingsLeftInOldPlace = mutableListOf<T>()
  private var thingsInTruck = mutableListOf<T>()
  private var thingsInNewPlace = mutableListOf<T>()

  // 4
  init {
    thingsLeftInOldPlace.addAll(thingsToMove)
  }

  // 5
  fun moveEverythingToTruck() {
    while (thingsLeftInOldPlace.count() > 0) {
      val item = thingsLeftInOldPlace.removeAt(0)
      thingsInTruck.add(item)
      println("Moved your $item to the truck!")
    }
  }

  // 6
  fun moveEverythingIntoNewPlace() {
    while (thingsInTruck.count() > 0) {
      val item = thingsInTruck.removeAt(0)
	  thingsInNewPlace.add(item)
      println("Moved your $item into your new place!")
    }
  }

  // 7
  fun finishMove() {
    println("OK, we finished! We were able to move your:${thingsInNewPlace.toBulletedList()}")
  }
}
class CheapThing(val name: String) {
  override fun toString(): String {
    return name
  }
}
val cheapThings = listOf(
    CheapThing("Cinder Block table"),
    CheapThing("Box of old books"),
    CheapThing("Ugly old couch")
)
val cheapMover = Mover(cheapThings)
cheapMover.moveEverythingToTruck()  
cheapMover.moveEverythingIntoNewPlace()
cheapMover.finishMove()
Moved your Cinder Block table to the truck!
Moved your Box of old books to the truck!
Moved your Ugly old couch to the truck!
Moved your Cinder Block table into your new place!
Moved your Box of old books into your new place!
Moved your Ugly old couch into your new place!
OK, we finished! We were able to move your:
 - Cinder Block table
 - Box of old books
 - Ugly old couch
class BreakableThing(
    val name: String,
    var isBroken: Boolean = false
) {
  fun smash() {
    isBroken = true
  }

  override fun toString(): String {
    return name
  }
}
val television = BreakableThing("Flat-Screen Television")
val breakableThings = listOf(
      television,
      BreakableThing("Mirror"),
      BreakableThing("Guitar")
  )
val expensiveMover = Mover(breakableThings)
expensiveMover.moveEverythingToTruck()
expensiveMover.moveEverythingIntoNewPlace()
expensiveMover.finishMove()
Moved your Flat-Screen Television to the truck!
Moved your Mirror to the truck!
Moved your Guitar to the truck!
Moved your Flat-Screen Television into your new place!
Moved your Mirror into your new place!
Moved your Guitar into your new place!
OK, we finished! We were able to move your:
 - Flat-Screen Television
 - Mirror
 - Guitar
television.smash()
fun moveEverythingToTruck() {
  while (thingsLeftInOldPlace.count() > 0) {
    val item = thingsLeftInOldPlace.removeAt(0)

    if (item is BreakableThing) {
      if (!item.isBroken) {
        thingsInTruck.add(item)
        println("Moved your $item to the truck!")
      } else {
        println("Could not move your $item to the truck")
      }
    } else {
      thingsInTruck.add(item)
      println("Moved your $item to the truck!")
    }
  }
}

㈡erfaces

㈡erfaces allow you to declare information about what something ,而不是它 作为一个类层次结构。

interface Checkable {
  fun checkIsOK(): Boolean
}
class Mover<T: Checkable>
private var thingsWhichFailedCheck = mutableListOf<T>()
fun moveEverythingToTruck() {
  while (thingsLeftInOldPlace.count() > 0) {
    val item = thingsLeftInOldPlace.removeAt(0)

    if (item.checkIsOK()) {
      thingsInTruck.add(item)
      println("Moved your $item to the truck!")
    } else {
      thingsWhichFailedCheck.add(item)
      println("Could not move your $item to the truck :[")
    }
  }
}
fun moveEverythingIntoNewPlace() {
  while (thingsInTruck.count() > 0) {
    val item = thingsInTruck.removeAt(0)
    if (item.checkIsOK()) {
	  thingsInNewPlace.add(item)
	  println("Moved your $item into your new place!")
	} else {
	  thingsWhichFailedCheck.add(item)
	  println("Could not move your $item into your new place :[")
    }
  }
}
fun finishMove() {
  println("OK, we finished! We were able to move your:${thingsInNewPlace.toBulletedList()}")
  if (thingsWhichFailedCheck.isNotEmpty()) {
	println("But we need to talk about your:${thingsWhichFailedCheck.toBulletedList()}")
  }
}
类型不匹配。必需:Checkable,找到:便宜
kmhe suqkupzc。 Majiabex:Bsajluxlo,Feexf:FCeaygleyh

class CheapThing(val name: String): Checkable
Class'dectionthing'不是任何抽象而不是实施抽象的Nikkishok
CNUJP'Foyaqgffffffl'GOJ ADGDHEKZ IWL HICCARE范oOcccarov HIWTUR MRENWOYOH

override fun checkIsOK(): Boolean = true
class BreakableThing(
    val name: String,
    var isBroken: Boolean = false
): Checkable
override fun checkIsOK(): Boolean {
  return !isBroken
}
Moved your Flat-Screen Television to the truck!
Moved your Mirror to the truck!
Moved your Guitar to the truck!
Could not move your Flat-Screen Television into your new place :[
Moved your Mirror into your new place!
Moved your Guitar into your new place!
OK, we finished! We were able to move your:
 - Mirror
 - Guitar

But we need to talk about your:
 - Flat-Screen Television

通用界面

通用接口是一个被限制为泛型类型的接口。读取它看起来似乎是一个略微的圆形定义,所以这在练习中是什么样的?继续使用移动的隐喻。

// 1
interface Container<T> {
  // 2
  fun canAddAnotherItem(): Boolean
  fun addItem(item: T)
  // 3
  fun canRemoveAnotherItem(): Boolean
  fun removeItem(): T
  // 4
  fun getAnother(): Container<T>
  // 5
  fun contents(): List<T>
}
private fun moveContainerToTruck(container: Container<T>) {
  thingsInTruck.add(container)
  println("Moved a container with your ${container.contents().toBulletedList()} to the truck!")
}
类型不匹配。必填:T,找到:容器<T>
Drha Rejjegbc。 PAMIIWAL:B,TUEBF:Xomfeumis<L>

private var thingsInTruck = mutableListOf<Any>()
fun moveEverythingToTruck(startingContainer: Container<T>?)
var currentContainer = startingContainer
currentContainer?.let { moveContainerToTruck(it) }
// 1
if (currentContainer != null) {
  // 2
  if (!currentContainer.canAddAnotherItem()) {
    moveContainerToTruck(currentContainer)
    currentContainer = currentContainer.getAnother()
  }
  // 3
  currentContainer.addItem(item)
  println("Packed your $item!")
} else {
  // 4
  thingsInTruck.add(item)
  println("Moved your $item to the truck!")
}

类型擦除

当通用类型传递到类或接口时,编译器实际保留有关通用约束的信息,而不是关于填充通用空白的具体类型的任何信息。这被称为 类型擦除.

类型不匹配:必填:T,发现:任何
BZDO GizdukXM:Labiuwej:S,Wouvhv:OQS

private fun tryToMoveItemIntoNewPlace(item: T) {
  if (item.checkIsOK()) {
    thingsInNewPlace.add(item)
    println("Moved your $item into your new place!")
  } else {
    thingsWhichFailedCheck.add(item)
    println("Could not move your $item into your new place :[")
  }
}
if (item is T) {}
无法检查擦除类型:T
Sikyab Pquzn Fog Orlqamde Az Ifehas MHGI:C

if (item is Container<T>) {}
无法检查擦除类型:容器<T>
Nohfew fpopr laj efyqudje ug ikiduh hmha:bafhoiziy<M>

明星预测

Replace the T in Container<T> with an asterisk:

if (item is Container<*>) {}
if (item is Container<*>) {
  val itemInContainer = item.removeItem()
}

Reified类型参数

Reified Generic类型参数允许您使用泛型类型,但保留有关该类型的信息。

inline fun <reified R> Iterable<*>.filterIsInstance(): List<R>
val breakableThings = thingsInTruck.filterIsInstance<BreakableThing>()

val items = thingsInTruck.filterIsInstance<T>()
无法使用“t”作为Reified类型参数。使用类。
Xaqnev Owu's',例如Ceicoec CFSA Tuwofihas。 ADA U JBEHQ EZFMUEW。

val containers = thingsInTruck.filterIsInstance<Container<*>>()
val containers = thingsInTruck.filterIsInstance<Container<T>>()
for (container in containers) {
  thingsInTruck.remove(container)
  while (container.canRemoveAnotherItem()) {
    val itemInContainer = container.removeItem()
    println("Unpacked your $itemInContainer!")
    tryToMoveItemIntoNewPlace(itemInContainer)
  }
}
while (thingsInTruck.count() > 0) {
  val item = thingsInTruck.removeAt(0) as? T
  if (item != null) {
    tryToMoveItemIntoNewPlace(item)
  } else {
    println("Something in the truck was not of the expected generic type: $item")
  }
}
// 1
class CardboardBox: Container<BreakableThing> {
  //2
  private var items = mutableListOf<BreakableThing>()

  override fun contents(): List<BreakableThing> {
    // 3
    return items.toList()
  }

  // 4
  override fun canAddAnotherItem(): Boolean {
    return items.count() < 2
  }

  override fun addItem(item: BreakableThing) {
    // 5
    items.add(item)
  }

  override fun canRemoveAnotherItem(): Boolean {
    // 6
    return items.count() > 0
  }

  override fun removeItem(): BreakableThing {
    // 7
    val lastItem = items.last()
    items.remove(lastItem)
    return lastItem
  }

  override fun getAnother(): Container<BreakableThing> {
    // 8
    return CardboardBox()
  }
}
cheapMover.moveEverythingToTruck(null)
expensiveMover.moveEverythingToTruck(CardboardBox())
Moved your Cinder Block table to the truck!
Moved your Box of old books to the truck!
Moved your Ugly old couch to the truck!
Moved your Cinder Block table into your new place!
Moved your Box of old books into your new place!
Moved your Ugly old couch into your new place!
OK, we finished! We were able to move your:
 - Cinder Block table
 - Box of old books
 - Ugly old couch
Packed your Flat-Screen Television!
Packed your Mirror!
Moved a container with your
 - Flat-Screen Television
 - Mirror
 to the truck!
Packed your Guitar!
Moved a container with your
 - Guitar
 to the truck!
Unpacked your Mirror!
Moved your Mirror into your new place!
Unpacked your Flat-Screen Television!
Could not move your Flat-Screen Television into your new place :[
Unpacked your Guitar!
Moved your Guitar into your new place!
OK, we finished! We were able to move your:
 - Mirror
 - Guitar

But we need to talk about your:
 - Flat-Screen Television

Generic type variance (a.k.a., in and out declarations)

期限 泛型类型方差 听起来很可怕复杂,当你第一次遇到它。这个概念是远不一样复杂,因为它的声音。

interface Container<out T>
类型参数t被声明为“out”,但在t型“in”位置中发生
Chqo Kenapicad B e ow d​​ows doskiheheheheheheheheh'euq'duyohlidt op'urja uy njfa b

interface Container<in T>
类型参数t被声明为“IN”,但在T型“OUT”位置中发生
xvvu futiwahap k ew zepmenib eh'在'sex irhress en'eix'pekicoyh eh bsli n

修改器'出'与'In'不相容
diqozoid'aup'ef ajyojgegefno fusk'ib'

interface Container<T>
val ints = listOf(1, 2, 3)
val numbers: List<Number> = ints
val moreInts: List<Int> = numbers
类型不匹配。必填:列表<Int>, Found: List<Number>
NTTO Raffuhqr。乔尼尔:jalr.<Efx>, Xeitq: Gucq<Rezgay>

val mutableInts = mutableListOf(1, 2, 3)
val mutableNumbers: MutableList<Number> = mutableInts
类型不匹配。必需:umablelist.<Number>, Found: MutableList<Int>
DGNA SEXDIBDM。 Votio.ardue:Legivciriyh.<Cumbil>, Seasp: RepumdoNexd<Ofz>

interface Comparable<in T> {
  operator fun compareTo(other: T): Int
}
fun compare(comparator: Comparable<Number>) {
  val int: Int = 1
  comparator.compareTo(int)
  val float: Float = 1.0f
  comparator.compareTo(float)
}
val intComparable: Comparable<Int> = comparator
intComparable.compareTo(int)
intComparable.compareTo(float)
类型不匹配。必填:int,发现:浮动
tmna nehburfj。 Tivaahik:Oyj,Suurq:Yhooy

挑战

关键点

泛型是一个棒状园话题,所以回顾一些在Kotlin中记住的最重要的事情:

然后去哪儿?

例如,您可以在此处在泛型上进行更详细的详细信息,我鼓励寻求其他擦除和方差等主题的其他资源,例如,查看Java和Kotlin中的方差工作方式之间的差异。

有一个技术问题?想报告一个错误吗? 您可以向书记论坛中的书籍的作者提出问题并报告错误 这里.

有反馈分享在线阅读体验吗? 如果您对UI,UX,高亮反馈,或者我们的在线读者的其他功能,你可以将它们发送给设计团队与下面的表格:

© 2021 Razeware LLC

您可以免费读取,本章的部分显示为 混淆了 文本。解开这本书,我们的书籍和视频的整个目录,具有raywenderlich.com专业订阅。

现在解锁

要突出或记笔记,您需要在订阅中拥有这本书或自行购买。