22

请参阅:我可以为 C# 中的匿名类指定一个有意义的名称吗?

在 C# 中,您可以编写:

var e = new { ID = 5, Name= "Prashant" };
assertEquals( 5, e.ID )

但在 Scala 中,我最终会写:

var e = (5, "Prashant")
assertEquals( 5, e._1 )

Scala 通过使用泛型来维护类型安全(C# 也是如此),但失去了每个字段名称的可读性,例如我使用“_1”而不是“ID”。

Scala中有这样的东西吗?

4

5 回答 5

23
object T {
  def main(args: Array[String]) {  
    val e = new { var id = 5; var name = "Prashant" }
    assert(e.id == 5)
  }
}

好吧,让我们把事情说清楚。这确实在 Scala 2.7 和 Scala 2.8 上使用了反射,因为e在这种情况下, 的类型是结构类型,Scala 通过反射进行处理。这是生成的代码,在清理时间(scalac -Xprint:cleanup):

package <empty> {
  final class T extends java.lang.Object with ScalaObject {
    private <synthetic> <static> var reflMethod$Cache1: java.lang.reflect.Method = null;
    private <synthetic> <static> var reflClass$Cache1: java.lang.Class = null;
    <synthetic> <static> def reflMethod$Method1(x$1: java.lang.Class): java.lang.reflect.Method = {
      if (T.this.reflMethod$Cache1.eq(null).||(T.this.reflClass$Cache1.ne(x$1)))
        {
          T.this.reflMethod$Cache1 = x$1.getMethod("id", Array[java.lang.Class]{});
          T.this.reflClass$Cache1 = x$1;
          ()
        };
      T.this.reflMethod$Cache1
    };
    @remote def $tag(): Int = scala.ScalaObject$class.$tag(T.this);
    def main(args: Array[java.lang.String]): Unit = {
      val e: java.lang.Object = {
        new T$$anon$1()
      };
      scala.this.Predef.assert(scala.Int.unbox({
        var exceptionResult1: java.lang.Object = _;
        try {
          exceptionResult1 = T.reflMethod$Method1(e.getClass()).invoke(e, Array[java.lang.Object]{})
        } catch {
          case ($1$ @ (_: java.lang.reflect.InvocationTargetException)) => {
            exceptionResult1 = throw $1$.getCause()
          }
        };
        exceptionResult1
      }.$asInstanceOf[java.lang.Integer]()).==(5))
    };
    def this(): object T = {
      T.super.this();
      ()
    }
  };
  final class T$$anon$1 extends java.lang.Object {
    private[this] var id: Int = _;
    <accessor> def id(): Int = T$$anon$1.this.id;
    <accessor> def id_=(x$1: Int): Unit = T$$anon$1.this.id = x$1;
    private[this] var name: java.lang.String = _;
    <accessor> def name(): java.lang.String = T$$anon$1.this.name;
    <accessor> def name_=(x$1: java.lang.String): Unit = T$$anon$1.this.name = x$1;
    def this(): T$$anon$1 = {
      T$$anon$1.this.id = 5;
      T$$anon$1.this.name = "Prashant";
      T$$anon$1.super.this();
      ()
    }
  }
}

正在进行一些缓存,但如果我在两者之间交替idname它就会使缓存失效。Scala 2.8 也进行反射和缓存,但它使用更高效的缓存技术,应该提供更好的整体性能。作为参考,这里是 Scala 2.8 的清理:

package <empty> {
  final class T extends java.lang.Object with ScalaObject {
    final private <synthetic> <static> var reflParams$Cache1: Array[java.lang.Class] = Array[java.lang.Class]{};
    @volatile
    private <synthetic> <static> var reflPoly$Cache1: scala.runtime.MethodCache = new scala.runtime.EmptyMethodCache();
    <synthetic> <static> def reflMethod$Method1(x$1: java.lang.Class): java.lang.reflect.Method = {
      var method1: java.lang.reflect.Method = T.reflPoly$Cache1.find(x$1);
      if (method1.ne(null))
        return method1
      else
        {
          method1 = x$1.getMethod("id", T.reflParams$Cache1);
          T.reflPoly$Cache1 = T.reflPoly$Cache1.add(x$1, method1);
          return method1
        }
    };
    def main(args: Array[java.lang.String]): Unit = {
      val e: java.lang.Object = {
        new T$$anon$1()
      };
      scala.this.Predef.assert(scala.Int.unbox({
        val qual1: java.lang.Object = e;
        {
          var exceptionResult1: java.lang.Object = _;
          try {
            exceptionResult1 = T.reflMethod$Method1(qual1.getClass()).invoke(qual1, Array[java.lang.Object]{})
          } catch {
            case ($1$ @ (_: java.lang.reflect.InvocationTargetException)) => {
              exceptionResult1 = throw $1$.getCause()
            }
          };
          exceptionResult1
        }.$asInstanceOf[java.lang.Integer]()
      }).==(5))
    };
    def this(): object T = {
      T.reflParams$Cache1 = Array[java.lang.Class]{};
      T.reflPoly$Cache1 = new scala.runtime.EmptyMethodCache();
      T.super.this();
      ()
    }
  };
  final class T$$anon$1 extends java.lang.Object {
    private[this] var id: Int = _;
    <accessor> def id(): Int = T$$anon$1.this.id;
    <accessor> def id_=(x$1: Int): Unit = T$$anon$1.this.id = x$1;
    private[this] var name: java.lang.String = _;
    <accessor> def name(): java.lang.String = T$$anon$1.this.name;
    <accessor> def name_=(x$1: java.lang.String): Unit = T$$anon$1.this.name = x$1;
    def this(): T$$anon$1 = {
      T$$anon$1.super.this();
      T$$anon$1.this.id = 5;
      T$$anon$1.this.name = "Prashant";
      ()
    }
  }
}
于 2009-12-01T17:54:47.500 回答
18

您还可以命名要分配的元组的各个部分,如下所示:

val (ID, Name) = (5, "Prashant")
assertEquals( 5, ID )

您也可以像这样使用它:

val (ID, Name, Age) = functionThatReturnsATuple3
println("ID: " + ID + ", age: " + Age)

当我第一次阅读_x语法时,我认为它很棒并且经常使用它。从那以后,我基本上不再使用它,因为当我不得不查看两个月前编写的代码时,我不得不花费大量时间来弄清楚 等的类型是_1什么_2。我想事后看来很明显它idpair._1.

这也可以在函数内部使用mapfilter例如:

val list: List[ (Int, String, Double) ] = ...
list map { case (id, name, time) => ... }
list filter { case (_, name, _) => name == "X" }

请注意,filter您可以将_s 用于您不会在函数主体中使用的元素。当浏览类似的代码以确定正在使用结构的哪些部分以及如何建立值时,这可能很有用。

于 2009-12-03T12:14:20.053 回答
9

我会简单地做一个案例类:

object Yo{
  case class E(id: Int, name: String)

  def main(){
    val e = E(5,"Prashant")
    println("name="+e.name+", id="+e.id)
  }
}

不确定它是否和丹尼尔的回答一样有效,但我希望它是一样的(我很感激对此发表评论)。无论如何,我发现它更具可读性,如果您有多个 if 实例,则仅使用共享的附加行E。您也可以向案例类添加方法,例如:

case class E(id: Int, name: String){
  def hasId(id: Int) = this.id==id
}

val e = E(5,"Prashant")
assert(e hasId 5)
于 2015-09-21T10:19:30.987 回答
4

正如 Juh_ 所建议的,扩展一个案例类应该做你想做的事:

scala> case class E(id: Int, name: String)
defined class E

scala> val e = new E(5, "Prashant")
e: E = E(5,Prashant)

scala> e.name
res3: String = Prashant

Case 类提供了一个 equals 方法,它们还扩展了Producttrait,这与 Scala 元组扩展的 trait 相同。也许在未来他们也会扩展ProductN特质

如果您按照其他答案中的建议使用匿名类,那么您最终不会得到真正的元组!例如,您没有得到 equals 方法:

scala> val x = new { val count = 5 }
x: AnyRef{val count: Int} = $anon$1@29ca901e

scala> val y = new { val count = 5 }
y: AnyRef{val count: Int} = $anon$1@1dfe2924

scala> x == y
res4: Boolean = false

从 Scala 2.11Tuple2开始,扩展确实有效,但这已被弃用,因为您不应该扩展案例类。

您也可以扩展Product2trait,但这不提供任何方法的实现,因此您必须自己编写所有方法。

您可能还可以使用 Shapeless HList,它会以添加外部依赖项为代价为您提供许多精美的功能。

我还尝试了 Twitter 的jaqen库,但在 Scala 2.11 中并没有为我编译。

我目前使用的是 Scala 2.11,所以我不能保证这个建议适用于其他版本的 Scala。

于 2016-08-30T15:29:51.867 回答
1

Scala 2.8 改进了类型系统,使其可以拥有静态和异构类型的数组和列表,因此大概可以对映射进行相同的操作。查看Jesper Nordenberg 关于“类型列表和异构类型数组”的博客以了解他的实现。

于 2009-12-01T17:44:32.540 回答