简体   繁体   English

Scala enum扩展特性

[英]Scala enum extending trait

I am writing a simple parser is Scala. 我正在写一个简单的解析器是Scala。

I have a base trait which represents an element in the file. 我有一个基本特征,代表文件中的一个元素。

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

This is what I need - the string (text) value and the parsed value (which sometimes will the the same string). 这就是我需要的 - 字符串(文本)值和解析的值(有时会是相同的字符串)。 Now I want to have a set of subclasses for: 现在我想要一组子类:

  • reserved symbols / keywords eg class , void etc. 保留的符号/关键字,例如classvoid等。
  • special symbols eg + , / etc. 特殊符号,例如+/
  • integer literals eg 123 整数文字,例如123
  • real literals eg 1.23 真正的文字,例如1.23
  • string literals eg '123' 字符串文字,例如'123'

How would you implement a hierarchy like this? 你会如何实现这样的层次结构? Since this is finite it'd be nice to use a case class.. I think. 由于这是有限的,所以使用案例类很好..我想。 But an enumeration would be great too .. How to combine? 但是枚举也会很棒..如何结合?


In other words, what's the best way to write this (below) in Scala in a more Scal-ish way? 换句话说,在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; }
}

etc. 等等

When building such a hierarchy in Scala, try to apply the following principles: 在Scala中构建此类层次结构时,请尝试应用以下原则:

  1. Sketch the class hierarchy you need. 绘制所需的类层次结构。 Design it in way that only leaf nodes are instantiated and the inner nodes are abstract. 设计它的方式是只实例化叶子节点,内部节点是抽象的。
  2. Implement the inner nodes as traits 将内部节点实现为特征
  3. Implement the leaf nodes as case classes 将叶节点实现为案例类

The reason for this is, that case classes add a lot of useful magic automatically (toString, unapply, serialize, equals, etc.). 原因是,case类自动添加了很多有用的魔法(toString,unapply,serialize,equals等)。 But the necessary code is generated in away that is not compatible with inheritance between case classes (eg equals would not work properly ). 但是必要的代码是在远程生成的,与case类之间的继承不兼容(例如equals不能正常工作 )。

Usually leaf types without parameters are usually modeled as case object while leaf types with parameters are modeled as case class . 通常,没有参数的叶子类型通常被建模为case object而带有参数的叶子类型被建模为case class

When you need to instantiate an inner node of the type tree, just add an artificial leaf and implement it as case class / case object. 当您需要实例化类型树的内部节点时,只需添加一个人工叶子并将其实现为案例类/案例对象。

You can also use Enumerations in Scala, but usually case classes are more practical. 您也可以在Scala中使用Enumerations ,但通常情况下的类更实用。 Enumerations are usually a good choice, when you need to convert a given string to the corresponding enumeration. 当您需要将给定字符串转换为相应的枚举时,枚举通常是一个不错的选择。 You can do this with Enumeration.withName(String) . 您可以使用Enumeration.withName(String)执行此操作。

In the answer from Alexey Romanov you can see how to apply this principles to a type tree with one root node and three leaf nodes. 在Alexey Romanov的回答中,您可以看到如何将此原则应用于具有一个根节点和三个叶节点的类型树。

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

    1.1. 1.1。 ClassSymbol (leaf node without parameters => case object) ClassSymbol (没有参数的叶节点=> case对象)

    1.2. 1.2。 VoidSymbol (leaf node without parameters => case object) VoidSymbol (没有参数的叶节点=> case对象)

    1.3. 1.3。 IntegerLiteral . IntegerLiteral (leaf node with parameters => case class) (带参数的叶子节点=>案例类)

An example for your situation, using both enums and case classes: 使用枚举和案例类的情况示例:

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
}

Some usage examples: 一些用法示例:

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