代码之家  ›  专栏  ›  技术社区  ›  waterlooalex

有没有一种方法可以在scala中使用命名字段的元组,类似于c中的匿名类?

  •  18
  • waterlooalex  · 技术社区  · 15 年前

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

    在C中,你可以写:

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

    但在斯卡拉,我写了:

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

    scala通过使用泛型(与c_一样)来维护类型安全,但会丢失每个字段名称的可读性,例如,我使用“_1”而不是“id”。

    斯卡拉有类似的东西吗?

    5 回复  |  直到 8 年前
        1
  •  19
  •   Daniel C. Sobral    15 年前
    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();
          ()
        }
      }
    }
    

    有一些缓存正在进行,但是如果我在 id name 它会使缓存失效。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";
          ()
        }
      }
    }
    
        2
  •  12
  •   ams    15 年前

    还可以命名要分配给的元组的各个部分,如:

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

    您也可以这样使用:

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

    当我第一次读到 _x 语法我觉得很好,经常使用。从那以后,我基本上就不再使用它了,因为当我必须查看两个月前编写的代码时,我必须花费大量的时间来计算 _1 ,请 _2 等等。我想事后看来很明显 id pair._1 .

    这也可以在类似的函数中使用 map , filter 如:

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

    注意在 滤波器 你可以使用 _ 对于您不打算在函数体中使用的元素。当浏览这样的代码以确定正在使用的结构部分以及如何构建值时,这可能很有用。

        3
  •  7
  •   Community Nick Dandoulakis    7 年前

    我只做一个案例类:

    object Yo{
      case class E(id: Int, name: String)
    
      def main(){
        val e = E(5,"Prashant")
        println("name="+e.name+", id="+e.id)
      }
    }
    

    不确定它是否像 Daniel's answer 但我希望是一样的(对此我很感激)。在任何情况下,我发现它更易读,如果您有多个实例,那么只使用一个共享的附加行 E . 还可以向case类添加方法,例如:

    case class E(id: Int, name: String){
      def hasId(id: Int) = this.id==id
    }
    
    val e = E(5,"Prashant")
    assert(e hasId 5)
    
        4
  •  5
  •   Community Nick Dandoulakis    7 年前

    正如Juh_u所建议的,扩展一个case类应该做您想要做的:

    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方法,它们还扩展了 Product 特征,这与scala元组扩展的特征相同。也许将来他们也会 extend the ProductN traits .

    如果你按照其他答案中的建议使用匿名类,你就不会得到真正的元组!例如,您不会得到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.11扩展 Tuple2 确实有效,但这已被弃用,因为不应该扩展case类。

    你也可以延长 Product2 特性,但它不提供任何方法的实现,所以您必须自己编写所有方法。

    你也可以用不定形的 HList 这将以添加外部依赖项为代价,为您提供许多奇特的特性。

    我也试过Twitter jaqen 库,但在scala 2.11中没有为我编译。

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

        5
  •  1
  •   Nathan Kleyn    9 年前

    scala 2.8改进了类型系统,使静态和异类类型的数组和列表成为可能,所以对map也可以这样做。退房 Jesper Nordenberg's blog on "Type Lists and Heterogeneously Typed Arrays" 为了他的实施。