我正在尝试使用如下参数化类型定义方法。
方法定义
def addNumber1[T](x:T):T = (x + 1.asInstanceOf[T])
但是得到如下错误..
并希望将其用于以下..
<console>:17: error: type mismatch;
found : T
required: String
def addNum[T](x:T):T = (x + 1.asInstanceOf [T])
^
addNumber1(100) // result = 101
addNumber1(100.005) // result = 101.005
addNumber1("One :") // result = "One :1"
请帮助
答案 0 :(得分:4)
第一条建议:尽可能远离asInstanceOf[]
。这被用作回避类型检查器的一种方式,这将导致运行时错误。类型检查器是你的朋友。不要把它刷掉。
当编译器在未定义类型上看到方法+
时,它假定您正在尝试执行某种String
操作。因此令人困惑的错误信息。
为了向参数化类型添加数字,您必须告诉编译器该类型仅限于Numeric
类型之一,即使这样,您也可以在可执行的操作中受到限制。 / p>
def addNumber1[T](x:T)(implicit ev: Numeric[T]):T = ev.plus(x, ev.one)
注意:这不会进行String
连接,但所有数字测试用例都应该通过。
注意:ev
代表&#34;证据&#34;,即如果有隐含证据表明T
是Numeric
,那么我们就可以对它进行一些数学运算。
答案 1 :(得分:1)
让我们从一个不同的角度来看待这个:实际上,是一个参数是什么?这是什么意思?
让我们从你可能更熟悉的东西开始:值参数。子程序中的值参数是什么意思?说,我们有这样的事情:
def foo(a, b) = { /* … */ } // deliberately ignoring types and the body
这意味着:我有一个包含两个参数foo
和a
的子例程b
。子例程 general ,它并不关心a
和b
的实际具体值是什么。它适用于a
和b
的所有值,因为它甚至知道 a
和b
的值def plus(a: Int, b: Int) = a + b
是。{/ p>
更具体一点:
plus
同样,a
并不知道b
和2
的实际值是什么。它适用于3
和23
以及42
和0
或0
和a
。它完全无视b
和a
的具体价值观。它只知道b
和blabla[A]
存在。
现在,类型参数是相同的,只是在类型级别而不是值级别。
blabla(a)
在类型级别意味着同样的事情,A
意味着在价值层面:&#34;我有一个东西,我不知道它是什么(我不在乎),但我会称之为def addNumber1[T](x: T): T = (x + 1.asInstanceOf[T])
。&#34;
现在,让我们来看看你的方法:
T
所以,你有效地做了什么,就是告诉Scala你对[{1}}一无所知。但是你用它做了一些事情(或者用它的实例):你向它添加1
。但是,你怎么知道可以添加它?你甚至都不知道它是什么!你明确地告诉了编译器:&#34;我有一些类型,我们称之为T
,这就是我们对该类型的了解。&#34;那么,你怎么知道这个类型甚至有一个+
方法,如果你对它一无所知呢?
所以,添加无法可能正常工作,必须失败!
然而,它失败的方式有点奇怪,并且与scala.Predef
对象中的一些预定义的隐式转换有关。特别是there is an implicit conversion for string concatenation, which can convert an arbitrary object into a String
and then concatenate another String
to it。在这种情况下,它会将x
转换为any2stringadd
,然后尝试将1
(或者1
强制转换为T
的实例)添加到其中,但是any2stringadd.+
方法只接受String
作为参数,因此您会得到一个奇怪的错误消息,即它期望String
。
当您遇到复杂的类型错误时,有时会弹出其他类似的类型:
Any
,AnyRef
,AnyVal
:这些类型位于Scala的类型层次结构的最顶层。有时候,如果你有一些编程错误,你认为你从两个不同的代码路径返回相同的类型,但实际上你返回两种不同的类型,Scala仍然会尝试推断两者之间的共同类型,并最终得到只有共同的祖先是Any
,AnyRef
或AnyVal
。 (这有点像Java或C♯中的Object
。)Serializable
:这实际上和上面一样。许多完全不同的类型实现了Serializable
接口,因此有时当你有两种非常不同的类型,你实际上期望相同的类型时,Scala会帮助推断Serializable
作为最接近的共同祖先,然后大喊大叫你用Serializable
来做一些它不支持的东西。Product
是所有Product
的超级特征(即Product1
,Product2
,... Product22
),因此所有Tuple
s(即Tuple1
,Tuple2
,... Tuple22
)。 Product
也混合在所有case class
es中。所以,如果你有两个Tuple
个不同的arity或两个不相关的case class
es(例如你有时会返回None
但是然后意外地返回somethingOfTypeFoo
而不是Some(somethingOfTypeFoo)
),那么Option
和case class Foo
之间最精确的常见类型将被推断为Product
或... Product with Serializable
:也可以收到上述内容的组合。例如。 Tuple
是Serializable
,因此此实际上是两个Tuple
不同arity中最精确的常见类型。遇到这些问题的一种常见方法是使用不带else
的条件表达式:
if (true) 42
这是什么类型的回归? Int
?没有! then
分支返回Int
,但else
分支返回 nothing (因为没有else
分支)。 Scala实际上有一个不返回任何内容的返回类型:Unit
。 Unit
是AnyVal
的子类型,Int
是AnyVal
的子类型,因此条件表达式的两个分支的类型最接近的共同祖先实际上是{{ 1}},而不是AnyVal
。 (注意:Int
分支无法访问的事实与打字角度无关。可达性是一个运行时事物,类型是编译时间。)
那么,我们如何解决您的问题?我们需要告诉编译器我们实际上做了解一下type参数。特别是,我们知道它是某种数字&#34;。在Scala中,有一个typeclass表示&#34; number&#34;,scala.math.Numeric
类型类的抽象概念。您可以使用以下内容:
else
这适用于&#34;类似数字&#34;:
的所有内容def addNumber1[T : Numeric](x: T): T = implicitly[Numeric[T]].plus(x, implicitly[Numeric[T]].one)
然而,你想要更加一般:
addNumber1(100) //=> 101
addNumber1(100.005) //=> 101.005
addNumber1("One :")
// error: could not find implicit value for evidence parameter of type Numeric[String]
不是String
类型类的实例(它不是&#34;类似数字&#34;)。 Scala中没有适合您账单的现成类型类。您必须自己编写,或者您可以查看Scalaz以查看是否有符合您需求的内容。