Ответ 1
Метод ++
определяется в ArrayOps
:
def ++[B](that: GenTraversableOnce[B]): Array[B]
Возвращает новую изменяемую индексированную последовательность, содержащую элементы из левый операнд, за которым следуют элементы правой руки операнд. Тип элемента изменяемой проиндексированной последовательности - это наиболее специфический суперкласс, охватывающий типы элементов двух операнды.
Выводится тип массива, в котором хранятся результаты.
('0' to '9').toArray ++ ('A' to 'Z').toArray
('0' to '9').toArray
отлично работает, и мы возвращаем Array[Char]
.
8: materializing requested scala.reflect.type.ClassTag[Char] using `package`
.this.materializeClassTag[Char]...
В соответствии с определением метода мы принимаем GenTraversableOnce[B]
. В нашем случае мы принимаем GenTraversableOnce[Char]
.
Если мы запустим scala с опцией -Ytyper-debug
, мы можем увидеть вывод типа в действии.
//it tries to find the type of arg0
typing '0'.to('1').toArray.$plus$plus('A'.to('B').toArray): pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode, silent=false, context.owner=value res1
typing '0'.to('1').toArray.$plus$plus: pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode BYVALmode POLYmode FUNmode, silent=true, context.owner=value res1
typing '0'.to('1').toArray: pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode POLYmode QUALmode, silent=true, context.owner=value res1
typing '0'.to('1'): pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode POLYmode QUALmode, silent=true, context.owner=value res1
typing '0'.to: pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode BYVALmode POLYmode FUNmode, silent=true, context.owner=value res1
typing '0': pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode POLYmode QUALmode, silent=true, context.owner=value res1
typed '0': Char('0')
adapted '0': Char to ?,
//It succeeds.
//Now it infers a scala type
infer implicit view {
tree '0'
pt Char('0') => ?{def to: ?}
}
new ImplicitSearch(floatWrapper) {...}
...
new ImplicitSearch(charWrapper) {...}
}
//The interesting bit...
typedImplicit1 charWrapper, pt=Char('0') => ?{def to: ?}, from implicit charWrapper:(c: Char)scala.runtime.RichChar
Implicit search yielded: SearchResult(scala.this.Predef.charWrapper, )
typed scala.this.Predef.charWrapper('0').to('1'): scala.collection.immutable.NumericRange.Inclusive[Char]
adapted scala.this.Predef.charWrapper('0').to('1'): scala.collection.immutable.NumericRange.Inclusive[Char] to ?,
typed scala.this.Predef.charWrapper('0').to('1').toArray: [B >: Char](implicit evidence$1: scala.reflect.ClassTag[B])Array[B]
//repeat the process
Infer implicit {
tree scala.this.Predef.charWrapper('0').to('1').toArray[Char]
pt scala.reflect.ClassTag[Char]
undetparams
}
Процесс вывода повторяется, и тип далее выводится из вызова метода этого типа, который равен ++
. Тип ('0' to '1').toArray ++
окончательно разрешен как ArrayOps
.
Implicit search yielded: SearchResult(scala.this.Predef.charArrayOps, )
adapted this.charArrayOps(charWrapper('0').to('1').toArray[Char](ClassTag.Char)).++: [B >: Char, That](that: scala.collection.GenTraversableOnce[B])(implicit bf: scala.collection.generic.CanBuildFrom[Array[Char],B,That])That to ?, undetparams=type B, type That
Scala затем набирает наш второй аргумент...
typed scala.this.Predef.charWrapper('A').to('B'): scala.collection.immutable.NumericRange.Inclusive[Char]
adapted scala.this.Predef.charWrapper('A').to('B'): scala.collection.immutable.NumericRange.Inclusive[Char] to ?,
typed scala.this.Predef.charWrapper('A').to('B').toArray: [B >: Char](implicit evidence$1: scala.reflect.ClassTag[B])Array[B]
Наконец, мы видим, что наш Array[B]
имеет черту GenTraversableOnce
.
infer implicit view {
tree <empty>
pt Array[?B] => scala.collection.GenTraversableOnce[?]
undetparams
}
Конечно, это не так, и мы получаем:
typing private[this] val res1: <error> = '0'.to('1').toArray.$plus$plus('A'.to('B').toArray): pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode BYVALmode, silent=false, context.owner=object $iw
typed private[this] val res1: <error> = scala.this.Predef.charArrayOps(scala.this.Predef.charWrapper('0').to('1').toArray[Char](ClassTag.Char)).++[B, That]('A'.to('B').toArray): <notype>
См. fooobar.com/questions/198755/... для получения информации о том, почему это так.
Решение состоит в использовании ++
:
def ++:[B >: T, That](that: collection.Traversable[B])(implicit bf: CanBuildFrom[Array[T], B, That]): That
Он отличается от ++ тем, что правый операнд определяет тип результирующая коллекция, а не левая. Мнемоника: КОЛОН находится на стороне нового типа COLUM.
Он также делает ArrayOps
от Arrays
, так что вам нужно.
Это подтверждается нашим вводом вывода отладки:
typing {
<synthetic> val x$1 = '0'.to('1').toArray;
'A'.to('B').toArray.$plus$plus$colon(x$1)
}:
...
typed scala.this.Predef.charWrapper('0').to('1').toArray: [B >: Char](implicit evidence$1: scala.reflect.ClassTag[B])Array[B]
...
typing scala.this.Predef.charArrayOps(scala.this.Predef.charWrapper('A').to('B').toArray(ClassTag.Char())).++:(scala.this.Predef.wrapCharArray(x$1), scala.this.Array.canBuildFrom(ClassTag.Char())): pt = Array[Char]: undetparams=, implicitsEnabled=false, enrichmentEnabled=false, mode=EXPRmode BYVALmode, silent=false, context.owner=value res2
...
<synthetic> val x$1: Array[Char] = new runtime.RichChar(scala.this.Predef.charWrapper('0')).to(scala.Char.box('1')).toArray(ClassTag.Char()).$asInstanceOf[Array[Char]]();
scala.this.Predef.charArrayOps(new runtime.RichChar(scala.this.Predef.charWrapper('A')).to(scala.Char.box('B')).toArray(ClassTag.Char()).$asInstanceOf[Array[Char]]()).++:(scala.this.Predef.wrapCharArray(x$1), scala.this.Array.canBuildFrom(ClassTag.Char())).$asInstanceOf[Array[Char]]()