隐式转换 vs 类型类

9 浏览
0 Comments

隐式转换 vs 类型类

在Scala中,我们可以使用至少两种方法来改造现有的或新的类型。假设我们想要表达可以使用Int进行量化的某个东西。我们可以定义以下特征。

隐式转换

trait Quantifiable{ def quantify: Int }

然后,我们可以使用隐式转换来对字符串和列表进行量化。

implicit def string2quant(s: String) = new Quantifiable{ 
  def quantify = s.size 
}
implicit def list2quantifiable[A](l: List[A]) = new Quantifiable{ 
  val quantify = l.size 
}

在导入这些后,我们可以在字符串和列表上调用方法quantify。请注意,可量化的列表存储其长度,因此它避免了在后续调用quantify时对列表的昂贵遍历。

类型类

另一种方法是定义一个“证明”Quantified[A],它说明某个类型A可以被量化。

trait Quantified[A] { def quantify(a: A): Int }

然后我们在某个地方为StringList提供这个类型类的实例。

implicit val stringQuantifiable = new Quantified[String] {
  def quantify(s: String) = s.size 
}

如果我们编写一个需要对其参数进行量化的方法,则写为:

def sumQuantities[A](as: List[A])(implicit ev: Quantified[A]) = 
  as.map(ev.quantify).sum

或使用上下文限定语法:

def sumQuantities[A: Quantified](as: List[A]) = 
  as.map(implicitly[Quantified[A]].quantify).sum

但是何时使用哪种方法?

现在是问题的关键。我如何在这两个概念之间进行选择?

到目前为止,我已经注意到以下事项。

类型类

  • 类型类允许使用上下文限定语法
  • 使用类型类时,我不会在每次使用时创建一个新的包装对象
  • 如果类型类具有多个类型参数,上下文限定语法将无法工作;想象一下,我不仅想用整数量化事物,还想用一些通用类型T的值量化。我希望创建一个类型类Quantified[A,T]

隐式转换

  • 由于我创建了一个新对象,我可以在其中缓存值或计算更好的表示;但是是否应该避免这样做,因为它可能会发生多次,并且显式转换可能只会调用一次?

我对答案的期望

提供一个(或多个)使用案例,其中区分了这两个概念的差异,并解释为什么我会更喜欢其中之一。即使没有例子,也希望解释这两个概念的本质及其相互关系。

0
0 Comments

从上面的内容中,我们可以看出出现了Implicit conversion vs. type class这个问题。问题出现的原因是因为在Scala中有两种不同的技术来实现类的扩展和功能增强,即implicit conversion和type class。

Implicit conversion是通过隐式转换来实现的,通过定义一个implicit方法,将一个类转换为另一个类,从而实现对原类的扩展和功能增强。例如,上面的代码中定义了一个trait Foo1,其中有一个foo方法,通过隐式转换可以将一个A类型的对象转换为Int类型。而Foo0则表示已经应用了隐式转换的结果,不再需要传入参数。通过这种方式,可以实现对类的扩展和功能增强。

Type class则是通过声明一个类型类来实现的,通过定义一个trait来表示一个类型类,并在需要增强功能的类中实现该trait,从而实现对类的扩展和功能增强。例如,上面的代码中定义了一个trait Foo1,其中有一个foo方法,表示对类的功能增强。通过这种方式,可以实现对类的扩展和功能增强。

问题的解决方法是根据需要选择使用implicit conversion还是type class来实现对类的扩展和功能增强。如果只需要简单的功能增强,可以使用implicit conversion;如果需要更复杂的功能增强,可以使用type class。根据具体的需求和情况选择合适的方式。

Implicit conversion vs. type class这个问题的出现是因为在Scala中有两种不同的技术来实现类的扩展和功能增强,即implicit conversion和type class。解决方法是根据需要选择合适的方式来实现对类的扩展和功能增强。

0
0 Comments

隐式转换和类型类是解决不同问题的两种不同的方法。隐式转换允许将一个类型转换为另一个类型,使代码看起来像调用一个方法。而类型类则允许为一个类型添加属性,而不是为类型的实例添加属性。

隐式转换的优点是可以使代码更加简洁和直观。通过隐式转换,可以将一个类型转换为另一个类型,使得代码看起来更像是调用一个方法。例如,可以通过隐式转换将字符串转换为具有新功能的对象,并直接调用该功能。但是,隐式转换可能会导致代码可读性下降,因为它可能会隐藏一些重要的类型转换。

类型类的优点是可以在类型级别上添加属性,并且可以在没有类型实例的情况下访问这些属性。这意味着可以为一个类型定义一组属性,并在需要时进行访问。例如,在上面的示例中,可以为列表类型定义一个默认值属性,并通过类型类访问该属性,而不需要一个具体的列表实例。这是通过定义一个类型类和相应的隐式实例来实现的。

隐式转换和类型类之间的区别在于它们解决的问题不同。隐式转换主要用于将一个类型转换为另一个类型,以提供更好的代码简洁性和可读性。而类型类主要用于在类型级别上添加属性,并在需要时进行访问。

要解决隐式转换和类型类的问题,可以根据具体的需求选择使用哪种方法。如果需要在类型级别上添加属性并在需要时进行访问,则可以使用类型类。如果只需要将一个类型转换为另一个类型,以提供更好的代码简洁性和可读性,则可以使用隐式转换。

总结起来,隐式转换和类型类是解决不同问题的两种不同的方法。隐式转换主要用于将一个类型转换为另一个类型,以提供更好的代码简洁性和可读性。而类型类主要用于在类型级别上添加属性,并在需要时进行访问。根据具体的需求选择使用哪种方法。

0
0 Comments

隐式转换与类型类之间的区别

在《Scala In Depth》一书中,我不想重复我的材料,但我认为值得注意的是,类型类/类型特征的灵活性更强。

def foo[T: TypeClass](t: T) = ...

具有在其局部环境中搜索默认类型类的能力。然而,我可以通过以下两种方式之一随时覆盖默认行为:

1. 创建/导入一个在作用域中的隐式类型类实例来绕过隐式查找;

2. 直接传递一个类型类。

下面是一个例子:

def myMethod(): Unit = {
   // 用于覆盖 Int 的默认隐式
   implicit object MyIntFoo extends Foo[Int] { ... }
   foo(5)
   foo(6) // 这些都使用了我覆盖的类型类
   foo(7)(new Foo[Int] { ... }) // 这个需要不同的配置
}

这使得类型类的灵活性更强。另一个优点是,类型类/特征更好地支持隐式查找。

在你的第一个例子中,如果你使用了一个隐式视图,编译器将对以下内容进行隐式查找:

Function1[Int, ?]

这将查找Function1的伴生对象和Int的伴生对象。

请注意,Quantifiable在隐式查找中没有出现。这意味着你必须将隐式视图放在包对象中,或者导入到作用域中。这需要更多的工作来记住发生了什么。

另一方面,类型类是显式的。你可以在方法签名中看到它在寻找什么。你还可以进行隐式查找:

Quantifiable[Int]

这将在Quantifiable的伴生对象和Int的伴生对象中查找。这意味着你可以提供默认值,并且新的类型(如MyString类)可以在其伴生对象中提供默认值,并且会被隐式搜索到。

一般来说,我使用类型类。对于初始的例子来说,它们的灵活性更强。我只在使用Scala包装器和Java库之间的API层时使用隐式转换,即使这样做也可能是“危险”的,如果不小心的话。

关于“无限灵活性”:对于隐式定义,我们也可以导入我们需要的确切转换,或者显式调用需要的转换。

0