2009-12-01 4 views
16

참조 :C#의 익명 클래스와 비슷한 스칼라에 명명 된 필드가있는 튜플을 사용할 수 있습니까?

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

을하지만 스칼라에서 내가 쓰기 결국 : Can I specify a meaningful name for an anonymous class in C#?

는 C#에서 당신이 쓸 수

var e = (5, "Prashant") 
assertEquals(5, e._1) 
스칼라는 C# .NET을처럼 (제네릭의 사용을 통해 안전을 입력 유지

), 각 필드 이름의 가독성을 잃습니다. 예를 들어 "ID"대신 "_1"을 사용합니다.

스칼라에는 이와 같은 것이 있습니까?

답변

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

좋아, 잘 정리 해보자. e의 유형이 구조 유형 인 스칼라 2.7 및 스칼라 2.8의 리플렉션을 사용합니다. 스칼라가 리플렉션을 통해 처리합니다.

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 번갈아 경우 이미 캐시를 무효화 것 : 여기 청소 시간 (scalac -Xprint:cleanup)에서 생성 된 코드입니다. Scala 2.8은 리플렉션 (reflection)과 캐싱 (caches)을 수행하지만 더 효율적인 캐싱 기술을 사용하므로 전반적인 성능이 향상됩니다.

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

당신은이를 사용할 수 있습니다 같이

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"; 
    () 
    } 
    } 
} 
+0

영리합니다. 이 id와 name 멤버를 가진 새로운 익명 AnyRef 서브 클래스를 생성합니까? – joev

+0

네, 그렇습니다. Scala 2.7, IIRC에서는 리플렉션을 사용하기 때문에 성능이 좋지 않습니다. 확실하지는 않지만 중요한 일이라면 그것을 확인해야합니다. –

+0

그 이유는 모르겠다. 컴파일러는 (익명) 클래스 정의에 액세스 할 수 있으므로 리플렉션을 할 필요가 없습니다. 구조적 타이핑 만이 내가 아는 한 반사의 사용을 수반합니다. –

1

스칼라 2.8은 유형 시스템을 개선하여 정적으로 유형이 다른 유형의 배열과 목록을 가질 수 있기 때문에 아마도 맵에서도 동일하게 수행 할 수 있습니다. 그의 구현을 위해 Jesper Nordenberg's blog on "Type Lists and Heterogeneously Typed Arrays"을 확인하십시오.

+0

자세히 설명해 주시겠습니까? Jesper의 게시물을 보면, Tuples를 scala로 구현하는 또 다른 방법 인 것처럼 보이지만, Tuple1, Tuple2, Tuple3 등을 가질 필요는 없습니다. 튜플 내의 필드 이름을 어떻게 처리 할 수 ​​있는지 아직 알 수 없습니다. –

+0

"아마도 ...지도로도 같은 작업을 수행 할 수 있습니다." 물론 독자의 연습 문제로 남았습니다. 어쨌든, 인스턴스가 생성 될 때 필드가 고정되면 다니엘 솔루션이 바람직합니다. –

+0

"done with maps"란 각 항목이 다른 유형이고 필드 이름이 키로 사용되는지도가 있다는 뜻입니까? –

11

당신은 또한 당신이 지정하고있는 튜플의 부분 이름을 지정할 수 있습니다 : 참고로, 여기 스칼라 2.8의 청소는 같은 : 내가 처음 _x 구문에 대해 읽을 때

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

나는 그것이 큰라고 생각하고 그것을 많이 사용. 나는 기본적으로 두 달 전에 쓴 코드를 봐야 할 때처럼 사용을 중단했다. _1, _2 등의 유형이 무엇인지 알아 내려고 노력해야한다. 나는 뒷얘기에서 idpair._1보다 훨씬 더 읽기 쉽다는 것이 확실하다고 생각합니다. filter 당신이 당신의 몸에 사용하지 않을 요소 _의를 사용할 수있는

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

참고 :

map, filter 등과 같은 같은 기능 내에서 사용할 수 있습니다 함수. 이와 같은 코드를 건너 뛰고 구조의 어떤 부분을 사용하고 값을 구성하는지 확인하는 데 유용 할 수 있습니다.

+0

재미있어 보이지만 투영 용으로 사용할 수 있습니까? 예 : 항목 목록을 명명 된 튜플에 매핑 하시겠습니까? –

+0

당신이 무엇을 의미하는지 잘 모르겠습니다. val list : List [(Int, String, Double)] = ...; list map {case (id, name, time) => ...} – ams

+0

C에서 # 할 수 있습니다 : employees.map (e => new {FirstName = e.Name.First, LastName = e.Name.Last}) .toList 예를 들어, 익명의 이름 클래스 목록에 직원 목록을 '프로젝트'합니다. 그래서 스칼라에서 할 수 있습니다 : employees.map (e => ((e.Name.First, e.Name.Last))). toList하지만 튜플의 필드 이름을 잃게됩니다. –

6

나는 단순히 케이스 클래스를 만들 것입니다 : 그것은 Daniel's answer만큼 효율적입니다

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

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

확실하지 경우를하지만 나는 (내가 그에 대한 의견을 감사하겠습니다) 동일 기대합니다. 어떤 경우에는 E 인 경우 하나 이상의 인스턴스가있는 경우 공유되는 추가 행 하나만 사용하면 더 읽기 쉽습니다.

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 

케이스 클래스가 equals 메소드에 제공하고, 그들이 : Juh_에서 알 수 있듯이

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

val e = E(5,"Prashant") 
assert(e hasId 5) 
+0

이것은 더 읽기 쉽지만 다양한 종류의 튜플을 생성하고 소비하는 무거운 컬렉션 변환에 더 많이 코드를 작성하고 읽기가 쉽지 않습니다. 이러한 경우 익명 객체가 여기에있는 인접 해답 중 하나에서 가장 잘 작동한다고 생각합니다. – matanster

+0

환경 설정의 문제입니다. 나는 (내가 동의하기 때문에) https://www.originate.com/library/scala-guide-best-practices 7 포인트 : "튜플을 과도하게 사용하지 말라." –

+0

명명 된 튜플 컬렉션을 주문할 수 있어야한다고 생각하지만 필드가 순서가 지정되지 않았으므로 (터플로 정렬 된 반면) 사례 클래스는 자동으로 정렬 할 수 없습니다. – combinatorist

4

, 케이스 클래스를 확장하는 것은 당신이 원하는 일을해야합니다 또한이 같은 경우 클래스에 메소드를 추가 할 수 있습니다 또한 Product 특성을 확장합니다. 이는 스칼라 튜플이 확장하는 것과 동일한 특성입니다. 어쩌면 미래에도 그들은 extend the ProductN traits 일 것입니다.

다른 답변에서 제안한 익명 클래스를 사용하면 실제 튜플이 생기지 않습니다! Tuple2이 작업을 수행 확장 스칼라 2.11으로

scala> val x = new { val count = 5 } 
x: AnyRef{val count: Int} = [email protected] 

scala> val y = new { val count = 5 } 
y: AnyRef{val count: Int} = [email protected] 

scala> x == y 
res4: Boolean = false 

하지만,이 경우 클래스를 확장 안하고 있기 때문에이되지 않습니다 예를 들어, 당신은 equals 메소드를하지 않습니다.

Product2 특성을 확장 할 수도 있지만 모든 방법의 구현을 제공하지 않으므로 모든 방법을 직접 작성해야합니다.

Shapeless HList을 사용하면 외부 종속성을 추가하는 비용으로 많은 멋진 기능을 제공 할 수 있습니다.

트위터의 jaqen 라이브러리도 사용해 보았지만, 스칼라 2.11에서는 컴파일되지 않았습니다.

현재 스칼라 2.11을 사용하고 있으므로이 조언이 다른 버전의 스칼라에도 적용된다고 보장 할 수 없습니다.