Scala初学者尝试使用参数化类型

时间:2016-09-04 06:40:33

标签: scala

我正在尝试使用如下参数化类型定义方法。

方法定义

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"

请帮助

2 个答案:

答案 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;,即如果有隐含证据表明TNumeric,那么我们就可以对它进行一些数学运算。

答案 1 :(得分:1)

让我们从一个不同的角度来看待这个:实际上,一个参数是什么?这是什么意思?

让我们从你可能更熟悉的东西开始:值参数。子程序中的值参数是什么意思?说,我们有这样的事情:

def foo(a, b) = { /* … */ } // deliberately ignoring types and the body

这意味着:我有一个包含两个参数fooa的子例程b。子例程 general ,它并不关心ab实际具体值是什么。它适用于ab所有值,因为它甚至知道 ab的值def plus(a: Int, b: Int) = a + b 是。{/ p>

更具体一点:

plus

同样,a并不知道b2的实际值是什么。它适用于323以及4200a。它完全无视ba的具体价值观。它只知道bblabla[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

当您遇到复杂的类型错误时,有时会弹出其他类似的类型:

  • AnyAnyRefAnyVal:这些类型位于Scala的类型层次结构的最顶层。有时候,如果你有一些编程错误,你认为你从两个不同的代码路径返回相同的类型,但实际上你返回两种不同的类型,Scala仍然会尝试推断两者之间的共同类型,并最终得到只有共同的祖先是AnyAnyRefAnyVal。 (这有点像Java或C♯中的Object。)
  • Serializable:这实际上和上面一样。许多完全不同的类型实现了Serializable接口,因此有时当你有两种非常不同的类型,你实际上期望相同的类型时,Scala会帮助推断Serializable作为最接近的共同祖先,然后大喊大叫你用Serializable来做一些它不支持的东西。
  • Product是所有Product的超级特征(即Product1Product2,... Product22),因此所有Tuple s(即Tuple1Tuple2,... Tuple22)。 Product也混合在所有case class es中。所以,如果你有两个Tuple个不同的arity或两个不相关的case class es(例如你有时会返回None但是然后意外地返回somethingOfTypeFoo而不是Some(somethingOfTypeFoo) ),那么Optioncase class Foo之间最精确的常见类型将被推断为Product或...
  • Product with Serializable:也可以收到上述内容的组合。例如。 TupleSerializable,因此实际上是两个Tuple不同arity中最精确的常见类型。

遇到这些问题的一种常见方法是使用不带else的条件表达式:

if (true) 42

这是什么类型的回归? Int?没有! then分支返回Int,但else分支返回 nothing (因为没有else分支)。 Scala实际上有一个不返回任何内容的返回类型:UnitUnitAnyVal的子类型,IntAnyVal的子类型,因此条件表达式的两个分支的类型最接近的共同祖先实际上是{{ 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以查看是否有符合您需求的内容。