【问题标题】:passing multiple datatypes to Type variable将多个数据类型传递给类型变量
【发布时间】:2023-03-08 15:17:01
【问题描述】:

我正在尝试传递 GrassRice 对象。但是,它编译失败。我根据此链接using Either 尝试了以下Either[] 选项。但是,它不起作用。

我想像这样限制传递 Fish 对象。我只想通过RiceGrass

(new Cow).eat(new Fish) // I don't want this to happen

请告诉我为什么Either 在这里不起作用。

object AbstractType {
    def main(args: Array[String]): Unit = {
        (new Cow).eat(new Grass) // Error here  -- type mismatch; found : Grass required: scala.util.Either[Grass,Rice]
    }
}

abstract class Animal{
    type FoodType <: Food
    def eat(food : FoodType)
}

class Food{}
class Grass extends Food
class Rice extends Food
class Fish extends Food{}

class Cow extends Animal{
    type FoodType = Either[Grass,Rice]  // instead of Either[], keeping Grass here is compiling successfully as expected.
    def eat(food : FoodType) {
        println("Cow eats")
    }
}

我按照slouc 的建议尝试了以下方法。但是,即使这种方法也无法限制这一点。 (new Cow).eat(Fish())

object AbstractType {
    def main(args: Array[String]): Unit = {
        (new Cow).eat(Grass())
    }
}
abstract class Animal{
    type FoodType <: Food
    def eat(food : FoodType)
}

sealed trait Food
final case class Grass() extends Food
final case class Rice() extends Food
final case class Fish() extends Food

class Cow extends Animal{
    type FoodType = //fill here 
    def eat(food : FoodType) {
        println("Cow eats")
    }
}

我的问题:填写上述代码的更好方法是什么,以便我只能传递 RiceGrass 对象。(如果没有,如何实现其他方式)并限制 @987654335 @对象。

【问题讨论】:

    标签: scala


    【解决方案1】:

    这可行:

    (new Cow).eat(Left[Grass, Rice](new Grass))
    

    但是您还有另一个问题 - 您定义了抽象类 Animal 以期望类型 FoodTypeFood 的子类型。 GrassRice 类型都是 Food 的单独有效子类型,但 Either[Grass, Rice] 不是。

    一点基础理论:

    当您使用从几种可能形式中取一种的类型时,这称为 sum 类型。与将所有给定类型组合成一个实体的 product type 不同(例如,Person 由字符串名字、字符串姓氏、整数年龄等组成),sum type 只从所有可能的实现中取出一个实现。这就是您所拥有的 - 您的 FoodType 是草、米饭或鱼。

    您的问题是您正在使用两种不同的构造来处理求和类型,这两种构造都用于建模求和类型的相同目的。一种方法是拥有一个特征或一个抽象类,然后通过所有可能的选项进行扩展:

    trait Food
    class Grass extends Food
    class Rice extends Food
    class Fish extends Food
    

    另一种方法是使用开箱即用的求和类型,例如EitherEither 的笨拙之处在于它只需要两种可能性,因此对于三种可能性,您必须拥有例如Either[Grass, Either[Rice, Fish]]。在一些常见的 Scala 库中,例如 scalaz 或 cat 中,还有其他更适合 sum 类型的构造(也称为“coproducts”),但我们现在不讨论。

    因此,您需要做的是决定是要坚持使用子类型化还是要使用 Either。对于您的用例,子类型完全没问题,所以只需删除 Either 并实现 type FoodType 为例如Grass 它会起作用,正如您在同一行的评论中指出的那样。

    顺便说一句,您的Food 是一个类,但请注意我所说的“特征或抽象类”。这是最佳实践原则;如果您不希望通过new Food 需要Food 本身的实例(而您不需要;您只会实例化它的子类,例如new Grass),那么最好不要允许这样首先实例化。

    另一个提示是创建这样的特征/抽象类sealed 和子类型final case class,这意味着没有其他人可以提供额外的选择(即引入一些自己的定制食物):

    sealed trait Food
    final case class Grass extends Food
    final case class Rice extends Food
    final case class Fish extends Food
    

    案例类(相对于标准类)服务器的目的是为您定义一些开箱即用的东西,例如

    • equals()、copy() 等方法
    • 支持模式匹配(通过为您实现应用/取消应用)
    • 默认伴随对象,允许你使用Grass()而不是new Grass()

    但是好吧,我有分歧 :) 希望这会有所帮助。

    编辑:

    好的,现在我意识到了你的实际问题。您需要引入另一种求和类型。你已经有Food,但现在你需要“牛粮”。您可以轻松地对其进行完全建模,添加一个扩展Food 并由GrassRice 扩展的CowFood 特征。

    sealed trait Food
    
    sealed trait CowFood extends Food
    sealed trait HorseFood extends Food
    sealed trait SealFood extends Food
    
    final case class Grass() extends CowFood with HorseFood
    final case class Rice() extends CowFood
    final case class Fish() extends SealFood
    
    ...
    
    type FoodType = CowFood
    

    (请记住,特征是可堆叠的;草既是牛食又是马食)

    我不是子类型化的忠实拥护者,但对于这个特殊的问题,它是一个比纠缠于 Eithers 和到处映射更简洁的解决方案。

    【讨论】:

    • 感谢使用“特征或抽象类”的建议。我尝试使用特征方法(修改后的问题)。请为我的编辑提出宝贵的建议。
    • @Praveen L 简单的例如有什么问题? type FoodType = Grass ?请注意,没有括号,这意味着您指的是类型本身而不是案例类。
    • @ slouc type FoodType = Grass 如果我们只想通过 Grass 绝对没问题。我需要这种方式的选择。 type FoodType = (Grass or Rice ) and (not Fish) (仅用于解释目的.....不是实际代码)。
    • 谢谢@slouc。正是我要找的。​​span>
    • @Praveen L 没问题,很高兴为您提供帮助!
    【解决方案2】:

    在上面的代码中,FoodType 被定义了两次,它在Cow 中的定义遮蔽了Animal 中的定义——这是两种不同的类型。在这种情况下,您不需要 Either。您可以使用Food 类型的参数定义eat 方法,并且只需传递GrassRiceFish,因为所有这些类都继承自Food

    该示例无法编译,因为它需要Either[Grass, Rice] 类型的参数,但Grass 类型的参数被传递。

    【讨论】:

    • 我不想像这样传递鱼食对象.. (new Cow).eat(new Fish)。因此,我想将值限制为 Rice 或 Grass。
    猜你喜欢
    • 2017-04-15
    • 2014-04-03
    • 2021-09-14
    • 2017-10-29
    • 2016-09-09
    • 1970-01-01
    • 1970-01-01
    • 2013-08-15
    • 1970-01-01
    相关资源
    最近更新 更多