有没有办法在 Scala 中使用带有命名字段的元组,类似于 C# 中的匿名类?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1827672/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 01:46:04  来源:igfitidea点击:

Is there a way to have tuples with named fields in Scala, similar to anonymous classes in C#?

c#scalatuples

提问by Alex Black

See: Can I specify a meaningful name for an anonymous class in C#?

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

In C# you can write:

在 C# 中,您可以编写:

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

But in Scala I end up writing:

但在 Scala 中,我最终写到:

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

Scala maintains type safety through the use of generics (as does C#), but loses the readability of the name of each field, e.g I use "_1" instead of "ID".

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

Is there anything like this in Scala?

Scala中有这样的东西吗?

回答by Daniel C. Sobral

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

Ok, let's make stuff clear. This does use reflection on Scala 2.7 and Scala 2.8, because the type of eis, in this case, a structural type, which Scala handles through reflection. Here is the generated code, at clean-up time (scalac -Xprint:cleanup):

好吧,让我们把事情说清楚。这确实在 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: java.lang.Class): java.lang.reflect.Method = {
      if (T.this.reflMethod$Cache1.eq(null).||(T.this.reflClass$Cache1.ne(x)))
        {
          T.this.reflMethod$Cache1 = x.getMethod("id", Array[java.lang.Class]{});
          T.this.reflClass$Cache1 = x;
          ()
        };
      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()
      };
      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 ($ @ (_: java.lang.reflect.InvocationTargetException)) => {
            exceptionResult1 = throw $.getCause()
          }
        };
        exceptionResult1
      }.$asInstanceOf[java.lang.Integer]()).==(5))
    };
    def this(): object T = {
      T.super.this();
      ()
    }
  };
  final class T$$anon extends java.lang.Object {
    private[this] var id: Int = _;
    <accessor> def id(): Int = T$$anon.this.id;
    <accessor> def id_=(x: Int): Unit = T$$anon.this.id = x;
    private[this] var name: java.lang.String = _;
    <accessor> def name(): java.lang.String = T$$anon.this.name;
    <accessor> def name_=(x: java.lang.String): Unit = T$$anon.this.name = x;
    def this(): T$$anon = {
      T$$anon.this.id = 5;
      T$$anon.this.name = "Prashant";
      T$$anon.super.this();
      ()
    }
  }
}

There is some caching going on, but if I alternated between idand nameit would invalidate the cache already. Scala 2.8 also does reflection, and also caches, but it uses a more efficient caching technique, which should provide better overall performance. For reference, here is the clean-up of Scala 2.8:

有一些缓存正在进行,但如果我在两者之间交替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: java.lang.Class): java.lang.reflect.Method = {
      var method1: java.lang.reflect.Method = T.reflPoly$Cache1.find(x);
      if (method1.ne(null))
        return method1
      else
        {
          method1 = x.getMethod("id", T.reflParams$Cache1);
          T.reflPoly$Cache1 = T.reflPoly$Cache1.add(x, method1);
          return method1
        }
    };
    def main(args: Array[java.lang.String]): Unit = {
      val e: java.lang.Object = {
        new T$$anon()
      };
      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 ($ @ (_: java.lang.reflect.InvocationTargetException)) => {
              exceptionResult1 = throw $.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 extends java.lang.Object {
    private[this] var id: Int = _;
    <accessor> def id(): Int = T$$anon.this.id;
    <accessor> def id_=(x: Int): Unit = T$$anon.this.id = x;
    private[this] var name: java.lang.String = _;
    <accessor> def name(): java.lang.String = T$$anon.this.name;
    <accessor> def name_=(x: java.lang.String): Unit = T$$anon.this.name = x;
    def this(): T$$anon = {
      T$$anon.super.this();
      T$$anon.this.id = 5;
      T$$anon.this.name = "Prashant";
      ()
    }
  }
}

回答by ams

You can also name the parts of the tuple you're assigning to, as in:

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

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

You can also use this like:

你也可以像这样使用它:

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

When I first read about the _xsyntax I thought it was great and used it a lot. I've since basically stopped using it as when I have to look at code I wrote two months ago I have to spend a load of time trying to work out what the types of the _1, _2etc. are. I suppose it's obvious in hindsight that idis much more readable than pair._1.

当我第一次阅读_x语法时,我认为它很棒并且经常使用它。因为我已经基本上使用它,当我要看看代码我写了两个月前我需要花时间的负载试图找出什么类型的停止_1_2等都是。我想事后看来很明显它idpair._1.

This can also be used inside functions like map, filteretc. like:

这也可以在诸如等的函数中使用mapfilter例如:

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

Note that in the filteryou can use _s for elements which you aren't going to use in the body of the function. This can be useful when skimming over code like that to establish what parts of structures are being used and how values are built up.

请注意,在s中,filter您可以将_s 用于不打算在函数体中使用的元素。这在浏览此类代码以确定正在使用结构的哪些部分以及如何构建值时非常有用。

回答by Juh_

I would simply make a case class:

我会简单地制作一个案例类:

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

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

Not sure if it is as efficient as Daniel's answerbut I expect it is the same (I'd appreciate comments on that). In any case, I find it more readable, using only one additional line that is shared if you have more than one instance if E. Also you could add method to the case class, such as:

不确定它是否和Daniel 的回答一样有效,但我希望它是一样的(我很感激对此发表评论)。无论如何,我发现它更具可读性,如果您有多个实例,则只使用一个额外的共享行 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)

回答by paulkernfeld

As Juh_ suggests, extending a case class should do what you want:

正如 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 classes provide an equals method, and they also extend the Producttrait, which is the same trait that Scala tuples extend. Maybe in the future they will also extend the ProductNtraits.

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

If you use anonymous classes as suggested in some other answers, you don't end up with real tuples! For example, you don't get the equals method:

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

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

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

scala> x == y
res4: Boolean = false

As of Scala 2.11 extending Tuple2does work, but this is deprecated because you're not supposed to extend case classes.

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

You could also extend the Product2trait, but that doesn't provide implementations of any methods, so you'd have to write all the methods yourself.

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

You could probably also use a Shapeless HList, which would give you lots of fancy features at the cost of adding an external dependency.

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

I also tried Twitter's jaqenlibrary, but that didn't compile for me in Scala 2.11.

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

I'm currently using Scala 2.11, so I can't guarantee that this advice applies to other versions of Scala.

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

回答by Randall Schulz

Scala 2.8 has improved the type system to the point that it is possible to have statically and heterogeneously typed arrays and lists, so presumably the same could be done with maps. Check out Jesper Nordenberg's blog on "Type Lists and Heterogeneously Typed Arrays"for his implementation.

Scala 2.8 将类型系统改进到可以拥有静态和异构类型的数组和列表,因此大概可以用映射来完成。查看Jesper Nordenberg 关于“类型列表和异构类型数组”的博客,了解他的实现。