복붙노트

[SCALA] 슈퍼 클래스와 케이스 클래스 복사 '방법'

SCALA

슈퍼 클래스와 케이스 클래스 복사 '방법'

나는 이런 일을하고 싶지 :

sealed abstract class Base(val myparam:String)

case class Foo(override val myparam:String) extends Base(myparam)
case class Bar(override val myparam:String) extends Base(myparam)

def getIt( a:Base ) = a.copy(myparam="changed")

나는 getIt의 맥락에서, 나는 모든 자료는 '사본'방법이 있지만, 사본 중 하나를 정말 방법이 아니다는 것을 컴파일러에게하지 않았기 때문에 그래서 나는 특성 또는 추상적 인 방법이 있다고 생각하지 않습니다 수 없습니다 내가 제대로이 일을하기 위해 자료에 넣을 수 있습니다. 또는, 거기?

나는 추상 클래스 자료로 자료를 정의하려고하면 {데프 (: 문자열을 사용하여 myParam 일) : 복사 자료}, 다음의 경우 클래스 푸 (하여 myParam : 문자열) 푸 추상적 할 필요가 수업 시간에 자료 결과를 확장을, 방법 복사 이후 유형의 수업 자료 (하여 myParam : 문자열) 자료가 정의되어 있지 않습니다

모든 기본 클래스 구현의 경우 클래스 될 것이라고 컴파일러에게 몇 가지 다른 방법이 있나요? 일부 특성이 수단 "이 경우 클래스의 속성이 있습니다"?

나는 자료는 경우 클래스가 만들 수있는,하지만 난 경우 클래스에서 상속이 말을 컴파일러 경고가되지 않습니다 얻을?

나는 수도 있습니다 알고 :

def getIt(f:Base)={ 
  (f.getClass.getConstructors.head).newInstance("yeah").asInstanceOf[Base]
}

하지만 ... 그것은 매우 추한 것 같다.

생각? 내 모든 접근 방식은 그냥 "잘못"인가?

UPDATE 나는 속성을 포함하도록 기본 클래스를 변경하고, 케이스 클래스는 "재정의"키워드를 사용했다. 이것은 더 나은 실제 문제를 반영하고 Edmondo1984의 반응을 고려하여 문제보다 현실적인 있습니다.

해결법

  1. ==============================

    1.질문이 변경되기 전에이, 오래된 대답이다.

    질문이 변경되기 전에이, 오래된 대답이다.

    강력한 형식의 프로그래밍 언어는 당신이 뭘 하려는지 방지 할 수 있습니다. 의 이유를 보자.

    다음 서명하는 방법의 아이디어 :

    def getIt( a:Base ) : Unit
    

    메소드의 몸이 기본 클래스 또는 인터페이스 만 기본 클래스 / 인터페이스 또는 부모에 정의 즉 속성과 방법을 통해 볼 속성에 액세스 할 수있을 것입니다. 코드를 실행하는 동안, 각각의 특정 인스턴스는 다른 서브 클래스를 가질 수있는 getIt 방법에 전달하지만,의 컴파일 유형은 항상 자료가 될 것입니다

    하나는 이런 식으로 추론 할 수 있습니다 :

    이 안전하지 않은 이유를 간단한 예를 보여줍니다 :

    sealed abstract class Base
    case class Foo(myparam:String) extends Base
    case class Bar(myparam:String) extends Base
    case class Evil(myEvilParam:String) extends Base
    
    def getIt( a:Base ) = a.copy(myparam="changed")
    

    컴파일러는 컴파일시에 오류가 발생하지 않은 경우 다음과 같은 경우, 그것은 코드가 런타임에 존재하지 않는 속성에 액세스하려고 할 것입니다 의미합니다. 이것은 엄격한 타입 프로그래밍 언어 수 없습니다 :이 극적으로 코드가 포함 할 수있는 버그의 수를 줄일 것을 알고, 컴파일러에 의해 코드의 훨씬 더 강력한 검증을 위해 쓸 수있는 코드에 제한을 거래 한

    이 새로운 답변입니다. 그것은 몇 가지 포인트는 결론에 도착하기 전에 필요한 길이 때문에 조금

    불행하게도, 당신은 클래스 당신이 제안하는 것을 구현하기 위해 복사 할 경우의 메커니즘에 의존 할 수 없다. 복사 방법은 작동 방법은 간단하게가 아닌 경우 클래스에서 자신을 구현할 수있는 복사 생성자입니다. 의이 경우 클래스를 생성하고 REPL에서 분해 보자 :

    scala>  case class MyClass(name:String, surname:String, myJob:String)
    defined class MyClass
    
    scala>  :javap MyClass
    Compiled from "<console>"
    public class MyClass extends java.lang.Object implements scala.ScalaObject,scala.Product,scala.Serializable{
        public scala.collection.Iterator productIterator();
        public scala.collection.Iterator productElements();
        public java.lang.String name();
        public java.lang.String surname();
        public java.lang.String myJob();
        public MyClass copy(java.lang.String, java.lang.String, java.lang.String);
        public java.lang.String copy$default$3();
        public java.lang.String copy$default$2();
        public java.lang.String copy$default$1();
        public int hashCode();
        public java.lang.String toString();
        public boolean equals(java.lang.Object);
        public java.lang.String productPrefix();
        public int productArity();
        public java.lang.Object productElement(int);
        public boolean canEqual(java.lang.Object);
        public MyClass(java.lang.String, java.lang.String, java.lang.String);
    }
    

    스칼라에서 복사 방법은 세 가지 매개 변수를 결국 당신은 지정하지 않은 사람의 현재 인스턴스에서 하나를 사용할 수 있습니다 (스칼라 언어는 메소드 호출에서 매개 변수의 기능 중 기본 값을 제공합니다)

    의 우리의 분석에 내려 가서 업데이트로 코드를 다시 보자 :

    sealed abstract class Base(val myparam:String)
    
    case class Foo(override val myparam:String) extends Base(myparam)
    case class Bar(override val myparam:String) extends Base(myparam)
    
    def getIt( a:Base ) = a.copy(myparam="changed")
    

    (:합니다 MyType가) 다음의 계약을 존중합니다 MyType 지금이 컴파일을하기 위해, 우리는 getIt의 서명에 사용해야합니다 :

    이 모든 방법이 적합 할 것입니다 :

      def copy(myParam:String) = null
      def copy(myParam:String, myParam2:String="hello") = null
      def copy(myParam:String,myParam2:Option[Option[Option[Double]]]=None) = null
    

    스칼라에서이 계약을 표현하는 방법은 없습니다,하지만 도움이 될 수있는 고급 기술이있다.

    우리가 할 수있는 첫 번째 관찰은 스칼라의 케이스 클래스와 튜플 사이의 엄격한 관계가 있다는 것입니다. 사실 케이스 클래스는 추가 행동과 명명 된 속성을 가진 튜플은 어떻게 든입니다.

    두 번째 관찰은 클래스 계층 구조의 특성 수를 동일하게 보장되지 않기 때문에, 복사 방법 서명이 동일하게 보장되지는 점이다.

    실제로, AnyTuple을 가정하여 [지능은 제 1 값이 타입은 int입니다 크기의 튜플, 우리는 그런 식으로 뭔가를 찾고 설명 :

    def copyTupleChangingFirstElement(myParam:AnyTuple[Int], newValue:Int) = myParam.copy(_1=newValue)
    

    모든 요소가 지능이라면 이것은 어려운 일 수 없다. 같은 유형의 모든 요소와 튜플은 목록이며, 우리는 목록의 첫 번째 요소를 교체하는 방법을 알고있다. 우리는 목록에 어떤 TupleX을 변환 할 첫 번째 요소를 교체하고 TupleX에 목록 다시 변환해야합니다. 그래, 우리는 X가 가정 할 수있는 모든 값에 대한 모든 컨버터를 작성해야합니다. 성가신하지만 어려운 일이 아니다.

    우리의 경우하지만, 모든 요소는 지능이다. 우리는 첫 번째 요소는 Int 인 경우 그들은 모두 같은 것처럼 요소가 서로 다른 타입 인 튜플을 치료합니다. 이것은 ... 불리운다

    즉, 독립적으로 크기, 일반적인 방법으로 서로 다른 크기의 튜플 처리. 그것을 위해, 우리는 이기종 유형을 지원하는 특별 목록, 이름 HList로 변환해야

    결론

    케이스 클래스 상속은 사용자가 메일 링리스트에서 여러 글에서 찾을 수있는, 아주 좋은 이유가되지 않습니다 : http://www.scala-lang.org/node/3289

    당신은 당신의 문제를 해결하기 위해 두 가지 전략이있다 :

  2. ==============================

    2.TL; DR : 나는 아직도 컴파일러 자동으로 파생 된 경우 클래스에서의 구현을 생성시키는 동안 자료의 복사 방법을 선언 할 수 있었다. 이것은 약간의 트릭을 포함한다 (실제로 나 자신이 바로 유형 계층 구조를 재 설계 거라고)하지만 적어도 당신이 참으로 파생 된 경우 클래스의에서 보일러 플레이트 코드를 작성하지 않고 작동 할 수 있습니다 보여 간다.

    TL; DR : 나는 아직도 컴파일러 자동으로 파생 된 경우 클래스에서의 구현을 생성시키는 동안 자료의 복사 방법을 선언 할 수 있었다. 이것은 약간의 트릭을 포함한다 (실제로 나 자신이 바로 유형 계층 구조를 재 설계 거라고)하지만 적어도 당신이 참으로 파생 된 경우 클래스의에서 보일러 플레이트 코드를 작성하지 않고 작동 할 수 있습니다 보여 간다.

    이미 론과 Edmondo1984 언급 먼저, 그리고 귀하의 경우 클래스가 다른 분야가 있다면, 당신은 문제로 얻을 수 있습니다.

    나는 엄격하지만 귀하의 예제에 충실하고, 모든 경우 클래스가 같은 필드가 있다고 가정합니다 (당신의 GitHub의 링크에서 찾고,이 역시 실제 코드의 경우 것 같다).

    모든 경우 클래스가 동일한 필드가 있음을 감안할 때, 자동 생성 된 복사 방법은 좋은 시작이다 동일한 서명을해야합니다. 당신이 그랬던 것처럼 그냥 자료의 일반적인 정의를 추가하려면 다음 합리적인 것 같다 : 추상 클래스베이스 {데프 복사 (하여 myParam : 문자열) : 자료} 문제는 이미 기본 클래스의 하나가 있기 때문에 스칼라, 복사 방법을 생성하지 않습니다 지금이다.

    그것은 정적 자료가 바로 복사 방법이 있는지 확인하는 또 다른 방법이 있다는 것을 밝혀, 그리고 구조적인 입력과 자기 형의 주석을 통해서이다 :

    type Copyable = { def copy(myParam: String): Base }
    sealed abstract class Base(val myParam: String) { this : Copyable => }
    

    그리고 우리의 이전의 시도와는 달리,이 자동 생성 복사 방법에 스칼라를 방지하지 않습니다. 마지막 문제가 있습니다 : 자기 형 주석은 자료의 하위 클래스가 복사 방법이 있는지 확인합니다,하지만 그것은 공개적으로 자료에 availabe하지 않습니다 :

    val foo: Base = Foo("hello")
    foo.copy()
    scala> error: value copy is not a member of Base
    

    이 문제를 해결하기 위해 우리는 복사 가능으로 자료에서 암시 적 변환을 추가 할 수 있습니다. 기초가 복사 가능이 보장 될 때 간단한 캐스트는 할 것입니다 :

    implicit def toCopyable( base: Base ): Base with Copyable = base.asInstanceOf[Base with Copyable]
    

    최대 포장이 우리를 제공합니다 :

    object Base {
      type Copyable = { def copy(myParam: String): Base }
      implicit def toCopyable( base: Base ): Base with Copyable = base.asInstanceOf[Base with Copyable]
    }
    sealed abstract class Base(val myParam: String) { this : Base. Copyable => }
    
    case class Foo(override val myParam: String) extends Base( myParam )
    case class Bar(override val myParam: String) extends Base( myParam )
    
    def getIt( a:Base ) = a.copy(myParam="changed")
    

    보너스 효과 : 우리가 다른 서명이있는 경우 클래스를 정의하려고하면, 우리는 컴파일 오류가 발생합니다 :

    case class Baz(override val myParam: String, truc: Int) extends Base( myParam ) 
    scala> error: illegal inheritance; self-type Baz does not conform to Base's selftype Base with Base.Copyable
    

    마무리까지 하나 개의 경고 : 당신은 아마 위의 트릭에 의존하는 것을 방지하기 위해 설계를 수정해야한다. 귀하의 경우에는, 론의 제안은 추가 ETYPE 필드와 하나의 경우 클래스는 합리적인보다 더 많은 것 같습니다 사용할 수 있습니다.

  3. ==============================

    3.서로 다른 필드를 가질 수 있도록 두 경우 클래스는 시간이 지남에 따라 분기 할 경우, 공유 복사 방법은 작업을 중단합니다.

    서로 다른 필드를 가질 수 있도록 두 경우 클래스는 시간이 지남에 따라 분기 할 경우, 공유 복사 방법은 작업을 중단합니다.

    자료 : 추상적 인 데프 withMyParam (: X newparam이)를 정의하는 것이 좋습니다. 더 나은, 당신이 돌아 왔을 경우 클래스 유형을 유지하기 위해 추상 형식을 도입 할 수 있습니다 :

    scala> trait T {
         |   type Sub <: T
         |   def myParam: String
         |   def withMyParam(newParam: String): Sub
         | }
    defined trait T
    
    scala> case class Foo(myParam: String) extends T {
         |   type Sub = Foo
         |   override def withMyParam(newParam: String) = this.copy(myParam = newParam)
         | }
    defined class Foo
    
    scala>
    
    scala> case class Bar(myParam: String) extends T {
         |   type Sub = Bar
         |   override def withMyParam(newParam: String) = this.copy(myParam = newParam)
         | }
    defined class Bar
    
    scala> Bar("hello").withMyParam("dolly")
    res0: Bar = Bar(dolly)
    
  4. ==============================

    4.이것은 나를 위해 잘 작동합니다 :

    이것은 나를 위해 잘 작동합니다 :

    sealed abstract class Base { def copy(myparam: String): Base }
    
    case class Foo(myparam:String) extends Base {
      override def copy(x: String = myparam) = Foo(x)
    }
    
    def copyBase(x: Base) = x.copy("changed")
    
    copyBase(Foo("abc")) //Foo(changed)
    
  5. ==============================

    5.나는이 확장 방법에 대한 어떤 생각합니다. 복사 방법 자체에 대한 구현 전략의 골라 봐.

    나는이 확장 방법에 대한 어떤 생각합니다. 복사 방법 자체에 대한 구현 전략의 골라 봐.

    나는 문제가 한 곳에서 해결하는 것이 여기에 좋아한다.

    그것은 caseness에 대한 특성이없는 이유를 물어 흥미로운 : 그것은 복사, 많이는 항상 인수없이 호출 할 수있는 것을 제외하고, 복사를 호출하는 방법에 대해 언급하지 않았다 ().

    sealed trait Base { def p1: String }
    
    case class Foo(val p1: String) extends Base
    case class Bar(val p1: String, p2: String) extends Base
    case class Rab(val p2: String, p1: String) extends Base
    case class Baz(val p1: String)(val p3: String = p1.reverse) extends Base
    
    object CopyCase extends App {
    
      implicit class Copy(val b: Base) extends AnyVal {
        def copy(p1: String): Base = b match {
          case foo: Foo => foo.copy(p1 = p1)
          case bar: Bar => bar.copy(p1 = p1)
          case rab: Rab => rab.copy(p1 = p1)
          case baz: Baz => baz.copy(p1 = p1)(p1.reverse)
        }
        //def copy(p1: String): Base = reflect invoke
        //def copy(p1: String): Base = macro xcopy
      }
    
      val f = Foo("param1")
      val g = f.copy(p1="param2") // normal
      val h: Base = Bar("A", "B")
      val j = h.copy("basic")     // enhanced
      println(List(f,g,h,j) mkString ", ")
    
      val bs = List(Foo("param1"), Bar("A","B"), Rab("A","B"), Baz("param3")())
      val vs = bs map (b => b copy (p1 = b.p1 * 2))
      println(vs)
    }
    

    그냥 재미를 위해, 반사 복사 :

      // finger exercise in the api
      def copy(p1: String): Base = {
        import scala.reflect.runtime.{ currentMirror => cm }
        import scala.reflect.runtime.universe._
        val im = cm.reflect(b)
        val ts = im.symbol.typeSignature
        val copySym = ts.member(newTermName("copy")).asMethod
        def element(p: Symbol): Any = (im reflectMethod ts.member(p.name).asMethod)()
        val args = for (ps <- copySym.params; p <- ps) yield {
          if (p.name.toString == "p1") p1 else element(p)
        }
        (im reflectMethod copySym)(args: _*).asInstanceOf[Base]
      }
    
  6. ==============================

    6.http://www.cakesolutions.net/teamblogs/copying-sealed-trait-instances-a-journey-through-generic-programming-and-shapeless에서이 사용 무형의 작업을 수행하는 방법에 대한 매우 포괄적 인 설명이있다; 링크 나누기 경우, 접근 방식은 자세한 내용을 발견하기에 충분해야한다 무형의 copySyntax 유틸리티를 사용합니다.

    http://www.cakesolutions.net/teamblogs/copying-sealed-trait-instances-a-journey-through-generic-programming-and-shapeless에서이 사용 무형의 작업을 수행하는 방법에 대한 매우 포괄적 인 설명이있다; 링크 나누기 경우, 접근 방식은 자세한 내용을 발견하기에 충분해야한다 무형의 copySyntax 유틸리티를 사용합니다.

  7. ==============================

    7.그 오래된 문제, 오래된 솔루션,

    그 오래된 문제, 오래된 솔루션,

    https://code.google.com/p/scala-scales/wiki/VirtualConstructorPreSIP

    케이스 클래스 복사 방법이 존재하기 전에했다.

    그래서이 문제에 대한 참조 각각의 경우 클래스는 이렇게 복사하고합니다 MyType / thisType 플러스 newThis 함수를 정의하면, 각각의 경우 클래스 수정 유형을 설정, 어쨌든 잎 노드이어야한다. 당신이 나무 / newThis 기능과 사용의 기본 매개 변수를 확대하려는 경우에는 이름을 변경해야합니다.

    로 제쳐두고 -이 구현하기 전에 개선하기 위해 마법 플러그인 컴파일러를 기다리고했지만 형 매크로는 마법 주스 수있다. 내 코드는 어디 가본 적이 이유에 대한 자세한 설명은 케빈의 명 AutoProxy의 목록에서 검색

  8. from https://stackoverflow.com/questions/12370244/case-class-copy-method-with-superclass by cc-by-sa and MIT license