加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 改变阶级状态的惯用方法

发布时间:2020-12-16 18:17:28 所属栏目:安全 来源:网络整理
导读:我有几个类都扩展了相同的特性,并且共享了应该改变其状态的相互功能.但是我想知道是否有更好的方法来实现相同的功能. 例如: trait Breedcase object Pincher extends Breedcase object Haski extends Breedtrait Foox{ def age: Int def addToAge(i: Int):
我有几个类都扩展了相同的特性,并且共享了应该改变其状态的相互功能.但是我想知道是否有更好的方法来实现相同的功能.

例如:

trait Breed
case object Pincher extends Breed
case object Haski extends Breed

trait Foox{
  def age: Int
  def addToAge(i: Int): Foox 
}

case class Dog(breed: Breed,age: Int) extends Foox
case class Person(name: String,age: Int) extends Foox

我希望addToAge将使用附加的int返回相同的对象,
当然我可以为每个类实现相同的,这与DRY规则相矛盾:

case class Dog(breed: Breed,age: Int) extends Foox{
  def addToAge(i: Int) = copy(age = age + i)
}
case class Person(name: String,age: Int) extends Foox{
  def addToAge(i:Int) = copy(age = age + i)
}

>有没有更好的方法来避免这种情况?
>是否有一个选项可以避免重新定义该年龄:在每个案例类中使用Int并保持其状态(年龄已在特征中定义)?

解决方法

一个可能的解决方案,可能涵盖一些用例,是使用无形库中的镜头:

import shapeless._

abstract class Foox[T](
  implicit l: MkFieldLens.Aux[T,Witness.`'age`.T,Int]
) {
  self: T =>
  final private val ageLens = lens[T] >> 'age

  def age: Int
  def addToAge(i: Int): T = ageLens.modify(self)(_ + i)
}

case class Dog(breed: Breed,age: Int) extends Foox[Dog]
case class Person(name: String,age: Int) extends Foox[Person]

请注意,要创建镜头,您需要隐式MkFieldLens,因此将Foox定义为抽象类而不是特征更容易.否则你必须在每个孩子中写一些代码以提供隐含的代码.

此外,我认为没有办法避免定义一个年龄:每个孩子的Int.在构造实例时,您必须以某种方式提供年龄,例如狗(Pincher,5),所以你必须在那里有年龄的构造函数参数.

更多解释:

借用Haskell Lens tutorial:

A lens is a first-class reference to a subpart of some data type. […]
Given a lens there are essentially three things you
might want to do

  1. View the subpart
  2. Modify the whole by changing the subpart
  3. Combine this lens with another lens to look even deeper

The first and the second give rise to the idea that lenses are getters
and setters like you might have on an object.

修改部分可用于实现我们想要的年龄.

无形库提供了一种漂亮的无样板语法,用于为案例类字段定义和使用镜头.我相信The code example in the documentation是不言自明的.

以下代码来自该示例:

final private val ageLens = lens[???] >> 'age
def age: Int
def addToAge(i: Int): ??? = ageLens.modify(self)(_ + i)

addToAge的返回类型应该是什么?它应该是从中调用此方法的子类的确切类型.这通常是通过F-bounded polymorphism实现的.所以我们有以下内容:

trait Foox[T] { self: T => // variation of F-bounded polymorphism

  final private val ageLens = lens[T] >> 'age

  def age: Int
  def addToAge(i: Int): T = ageLens.modify(self)(_ + i)
}

在那里使用T作为孩子的确切类型,并且扩展Foox [T]的每个类应该将其自身提供为T(因为自我类型声明self:T =>).例如:

case class Dog(/* ... */) extends Foox[Dog]

现在我们需要制作那个镜头[T]>> ‘年龄线工作.

让我们分析>>的签名查看它需要运行的方法:

def >>(k: Witness)(implicit mkLens: MkFieldLens[A,k.T]): Lens[S,mkLens.Elem]

>我们看到’年龄论证被隐含地转换为无形.见证.见证表示特定值的确切类型,或者换言之,表示类型级别值.两种不同的文字,例如符号的年龄和’foo,有不同的证人,因此可以区分它们的类型.

Shapeless提供了一种花哨的反引号语法来获得一些值得见证的东西.对于’年龄符号:

Witness.`'age`    // Witness object
Witness.`'age`.T  // Specific type of the 'age symbol

>从第1项开始,>>签名,我们需要有一个隐含的MkFieldLens,对于类T(子案例类)和字段’age:

MkFieldLens[T,Witness.`'age`.T]

age字段也应该具有Int类型.可以用无形的Aux pattern来表达这个要求:

MkFieldLens.Aux[T,Int]

并且为了更自然地提供这种隐式,作为隐式参数,我们必须使用抽象类而不是特征.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读