繁体   English   中英

Scala enum扩展特性

[英]Scala enum extending trait

我正在写一个简单的解析器是Scala。

我有一个基本特征,代表文件中的一个元素。

trait Token[T] {
    def stringValue: String
    def value: T
}

这就是我需要的 - 字符串(文本)值和解析的值(有时会是相同的字符串)。 现在我想要一组子类:

  • 保留的符号/关键字,例如classvoid等。
  • 特殊符号,例如+/
  • 整数文字,例如123
  • 真正的文字,例如1.23
  • 字符串文字,例如'123'

你会如何实现这样的层次结构? 由于这是有限的,所以使用案例类很好..我想。 但是枚举也会很棒..如何结合?


换句话说,在Scala中以更加Scal-ish的方式编写这个(下面)的最佳方法是什么?

public interface Token<T> {
    String stringValue();
    T value();
}

public enum ReservedSymbol implements Token<ReservedSymbol> {
    CLASS('class'), VOID('void');

    private String val;
    private ReservedSymbol(String val) { this.val = val; }

    public String stringValue() { return val; }
    public ReservedSymbol value() { return this; }
}


public class IntegerLiteral implements Token<Integer> {       
    private Integer val;
    public IntegerLiteral(String val) { this.val = Integer.valueOf(val); }

    public String stringValue() { return val.toString(); }
    public Integer value() { return val; }
}

等等

在Scala中构建此类层次结构时,请尝试应用以下原则:

  1. 绘制所需的类层次结构。 设计它的方式是只实例化叶子节点,内部节点是抽象的。
  2. 将内部节点实现为特征
  3. 将叶节点实现为案例类

原因是,case类自动添加了很多有用的魔法(toString,unapply,serialize,equals等)。 但是必要的代码是在远程生成的,与case类之间的继承不兼容(例如equals不能正常工作 )。

通常,没有参数的叶子类型通常被建模为case object而带有参数的叶子类型被建模为case class

当您需要实例化类型树的内部节点时,只需添加一个人工叶子并将其实现为案例类/案例对象。

您也可以在Scala中使用Enumerations ,但通常情况下的类更实用。 当您需要将给定字符串转换为相应的枚举时,枚举通常是一个不错的选择。 您可以使用Enumeration.withName(String)执行此操作。

在Alexey Romanov的回答中,您可以看到如何将此原则应用于具有一个根节点和三个叶节点的类型树。

  1. Token (内部节点=>特征)

    1.1。 ClassSymbol (没有参数的叶节点=> case对象)

    1.2。 VoidSymbol (没有参数的叶节点=> case对象)

    1.3。 IntegerLiteral (带参数的叶子节点=>案例类)

使用枚举和案例类的情况示例:

trait Token[T]{
  def stringValue: String 
  def value: T
}

object ReservedSymbolEnum extends Enumeration {
  type ReservedSymbolEnum = Value
  val `class`, `void` = Value
      val NullValue = Value("null") // Alternative without quoting
}

case class ReservedSymbol(override val stringValue: String)extends Token[ReservedSymbolEnum.ReservedSymbolEnum] {
  def value = ReservedSymbolEnum.withName(stringValue)
}

case class StringLiteral(override val stringValue: String) extends Token[String] {
  override def value = stringValue
}

case class IntegerLitaral(override val stringValue: String) extends Token[Int] {
  override def value = stringValue.toInt
}

一些用法示例:

scala> def `void`=ReservedSymbol("void")
void: ReservedSymbol

scala> `void`.value
res1: ReservedSymbolEnum.Value = void

scala> def `42`=IntegerLiteral("42")
42: IntegerLitaral

scala> `42`.value
res2: Int = 42
sealed trait Token[+T] { // sealed means it only can be extended in this file
  def stringValue: String
  def value: T
}

// cast can be avoided if you are happy with extending Token[ReservedSymbol]
// as in the Java example
// class instead of trait so that it can have a constructor argument
sealed class ReservedSymbol[+T <: ReservedSymbol[T]](val stringValue: String) extends Token[T] {
  def value = this.asInstanceOf[T] 
}

// no body necessary
case object ClassSymbol extends ReservedSymbol[ClassSymbol]("class")
case object VoidSymbol extends ReservedSymbol[VoidSymbol]("void")

case class IntegerLiteral(val: Int) extends Token[Int] { ... }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM