Scala приведение к типу generi c - PullRequest
3 голосов
/ 04 марта 2020

Я не совсем понимаю тип generi c. Я ожидаю, что 2.asInstanceOf[A] приведен к типу A, между тем, он приведен к Int. Кроме того, входное значение равно java.lang.Long, тогда как выходное значение представляет собой список Int (согласно определению входные и выходные данные должны быть одного типа). Почему это так?

def whatever[A](x: A): List[A] = {
  val two = 2.asInstanceOf[A]
  val l = List(1.asInstanceOf[A],2.asInstanceOf[A])

  println(f"Input type inside the function for 15L: ${x.getClass}")
  println(f"The class of two: ${two.getClass}, the value of two: $two")
  println(f"The class of the first element of l: ${l.head.getClass}, first element value: ${l.head}")
  l
}

println(f"Returned from whatever function: ${whatever(15L)}")

Результат:

Input type inside the function for 15L: class java.lang.Long
The class of two: class java.lang.Integer, the value of two: 2
The class of the first element of l: class java.lang.Integer, first element value: 1
Returned from whatever function: List(1, 2)

Ответы [ 3 ]

12 голосов
/ 04 марта 2020

a.asInstanceOf[B] означает:

Уважаемый компилятор;

Пожалуйста, забудьте, что вы думаете о типе a. Я знаю лучше Я знаю, что если a не на самом деле типа B, тогда моя программа может взорваться, но я действительно очень умный, и этого не произойдет.

С уважением, ваша Super Programmer

Другими словами val b:B = a.asInstanceOf[B] не создаст новую переменную типа B, он создаст новую переменную, которая будет обрабатываться , как если бы это был тип B. Если фактический базовый тип a совместим с типом B, то все в порядке. Если реальный тип a несовместим с B, то все взрывается.

2 голосов
/ 04 марта 2020

Тип стирания . Для целей проверки типов 2 приводится к A; но на более поздней стадии компиляции A стирается до Object, поэтому ваш код становится эквивалентным

def whatever(x: Object): List[Object] = {
  val two = 2.asInstanceOf[Object]
  val l = List(1.asInstanceOf[Object],2.asInstanceOf[Object])

  println(f"Input type inside the function for 15L: ${x.getClass}")
  println(f"The class of two: ${two.getClass}, the value of two: $two")
  println(f"The class of the first element of l: ${l.head.getClass}, first element value: ${l.head}")
  l
}

2.asInstanceOf[Object] - это операция бокса , возвращающая java.lang.Integer.

Если вы попытаетесь на самом деле использовать возвращаемое значение в качестве List[Long], вы в конечном итоге получите ClassCastException, например,

val list = whatever(15L)
val x = list(0)

x будет предполагается, что он равен Long, а приведение вставлено, чтобы распаковать ожидаемый java.lang.Long.

1 голос
/ 04 марта 2020

Ответ от @jwvh на месте. Здесь я добавлю решение только в том случае, если вы хотите решить проблему безопасного преобразования Int в A в whatever, не зная, что такое A. Это, конечно, возможно, только если вы предоставите способ построить конкретный A из Int. Мы можем сделать это с помощью класса типов:

trait BuildableFromInt[+A] {
  def fromInt(i: Int): A
}

Теперь вам нужно только неявно предоставить BuildableFromInt для любого типа A, который вы sh будете использовать в whatever:

object BuildableFromInt {
    implicit val longFromInt: BuildableFromInt[Long] = Long.box(_)
}

и теперь определите, что принимать только совместимые типы A:

def whatever[A : BuildableFromInt](x: A): List[A] = {
  val two = implicitly[BuildableFromInt[A]].fromInt(2)
  // Use two like any other "A"
  // ...
}

Теперь все, что можно использовать с любым типом, для которого доступен BuildableFromInt.

...