Scalaのコヒーレンス(+),インバータ(-),上界(<br>:)

6821 ワード

Scalaのコヒーレンス(+),逆(-),上界(<:),下界(>:)
コヒーレントcovariant、インバータcontravariant、可変invariant
リスト[T]のようなバンドタイプパラメータのタイプについては、AおよびそのサブタイプBに対して、リスト[B]を満たしてもリスト[A]のサブタイプに該当する場合、covariance(コヒーレンス)と呼ばれ、リスト[A]がリスト[B]のサブタイプである場合、すなわち元の親子関係とは正反対である場合、contravariance(逆)と呼ばれる.
1つのタイプがコヒーレントまたはインバータをサポートする場合、このタイプをvariance(可変または変形に翻訳)と呼び、そうでなければinvariance(可変)と呼ぶ.
Javaでは、汎用タイプはinvariantであり、例えばListはListのサブタイプではない.
scalaは、タイプを定義するときに宣言することができます(プラス記号でコヒーレント、マイナス記号でインバータを表します).
trait List[+T] //            

これにより、List[String]は、List[Any]のサブタイプとして扱われる.
ただしJavaでは、変数を宣言するときに使用(use-site variance)がサポートされています.
List<? extends Object> list = new ArrayList<String>();

次のコード例のように
List<String> aList = new ArrayList<String>();
List<? extends Object> covariantList = aList;
List<? super String> contravariantList = aList;
covariantList.add("d"); //wrong
Object a = covariantList.get(0);

contravariantList.add("d"); //OK
String b = contravariantList.get(1); //wrong
Object c = contravariantList.get(2);

Javaの上界と下界
extendsキーワードを使用してパラメトリックタイプの上界を決定する
ここでパラメトリックタイプはDateであり,Dateのサブクラスである可能性もあるためaddメソッドは制限される.
@Test
public void upperBound(List<? extends Date> list, Date date) {
    Date now = list.get(0);
    System.out.println("now==>" + now);
    //list.add(date); //       ,        list   java.util.Date           
    list.add(null);//      ,  null      
}

superキーワードを使用してパラメトリックタイプの下限を決定する
ここで、パラメトリックタイプはTimestampである可能性があり、親(Objectを含む)である可能性がある
public void lowerBound(List<? super Timestamp> list) {
    Timestamp now = new Timestamp(System.currentTimeMillis());
    list.add(now);
    //Timestamp time = list.get(0); //    。            Date   Object,          Timestamp,         
}

Scalaのコヒーレントとインバータ
object app_main_1 extends App {
  val t: Temp[Super] = new Temp[Sub]("hello world")
  print(t.toString)
}

class Temp[+A](title: String)

//    

class Super

class Sub extends Super

ただし、variance(変形)は継承されず、親はvariance(可変タイプ)と宣言し、子は保持するには可変タイプとして宣言する必要があります.
以下に示すように、
scala> trait A[+T]
defined trait A

scala> class C[T] extends A[T]
defined class C

scala> class X; class Y extends X;
defined class X
defined class Y

scala> val c:C[X] = new C[Y]
<console>:11: error: type mismatch;
 found   : C[Y]
 required: C[X]
Note: Y <: X, but class C is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
       val c:C[X] = new C[Y]
                    ^

scala>

You may wish to define T as +T instead. (SLS 4.5)
次のように書くべきです.
scala> class C[+T] extends A[T]
defined class C

scala> val c:C[X] = new C[Y]
c: C[X] = C@7241a47d

scala>

Scalaインバータとコヒーレントの例の分析
Scala(および他の多くのプログラミング言語)では、関数もオブジェクトであり、他のオブジェクトを使用、定義できる場所でもあり、関数を使用、定義することもできます.Scalaの関数は,applyメソッドを持つクラスのインスタンスであり,関数として利用できる.ここでapplyが受け入れるパラメータは関数のパラメータであり、applyの戻り値は関数の戻り値である.
まず,パラメータを受け入れる関数の汎用定義を与える.
trait Function1[-T, +U] {
  def apply(x: T): U
}

この関数はパラメータを受け入れ,パラメータタイプは汎用タイプT,戻りタイプは汎用タイプUである.他の汎用言語と同様に、実際に関数を定義する場合、TとUのタイプが決定されますが、こちらのTの前に「-」があり、Uの前に「+」があることに注意してください.
ここでこの符号についての説明を導入し,Scalaの汎用型を宣言するとき,「+」はコヒーレンスを表し,「−」は逆を表す
  • C[+T]:AがBのサブクラスである場合、C[A]はC[B]のサブクラスである.>>>>>コヒーレント
  • C[-T]:AがBのサブクラスである場合、C[B]はC[A]のサブクラスである.>>>>インバータ
  • C[T]:AとBがどんな関係であっても、C[A]とC[B]には従属関係がない.

  • Liskov置換の原則によれば,AがBのサブクラスであれば,Bのすべての操作に適用でき,Aに適用できる.ここでFunction 1の定義が、このような条件を満たしているかどうかを見てみましょう.BirdがAnimalのサブクラスであると仮定すると、次の2つの関数の関係を見てみましょう.
    def f1(x: Bird): Animal//instance of Function1[Bird, Animal]
    def f2(x: Animal): Bird//instance of Function1[Animal, Bird]
    ここでf 2のタイプはf 1のタイプのサブクラスである.どうして?
    まずパラメータタイプを見てみると,Liskov置換の原則に従ってf 1が許容できるパラメータ,f 2も許容できる.ここで、f 1が受け入れるBirdタイプは、Birdオブジェクトが親アニマルのオブジェクトとして使用されることができるため、f 2が受け入れられることは明らかである.
    また、戻りタイプを見ると、f 1の戻り値はAnimalのインスタンスとして使用することができ、f 2の戻り値はBirdのインスタンスとして使用することができ、もちろんAnimalのインスタンスとして使用することもできる.
    したがって,関数のパラメータタイプは逆であり,関数の戻りタイプはコヒーレントであると述べた.
    関数のパラメータタイプはインバータであり,関数の戻りタイプはコヒーレントである.
    では、Scalaクラスを定義するときに、汎用タイプをコヒーレントまたはインバータとして勝手に指定することができますか?答えは否定的だ.以上の例から分かるように,Function 1のパラメータタイプをコヒーレントと定義したり,戻りタイプをインバータと定義したりするとLiskov置換の原則に反するため,Scalaではコヒーレントタイプはメソッドの戻りタイプとしてのみ,インバータタイプはメソッドのパラメータタイプとしてのみ利用できると規定している.類比関数の挙動は,Liskov置換の原則と結びつけて,このような規定が非常に合理的であることが分かった.
    この関数の汎用特性は関数式プログラミングに非常に有用である.C++の汎用型は文法的にコヒーレントとインバータをサポートしないが,C++11のfunctionでは,戻り型Uとパラメータ型Tも同様にScalaと同じコヒーレントとインバータ規則に従う.
    注:リ氏置換の原則では、いかなるベースクラスが現れる場所でも、サブクラスは必ず現れると言われています. 
    参照先:http://blog.csdn.net/oopsoom/article/details/24773239
    1.コヒーレント
    [+T],covariant(or"flexible")in its type parameter Tは,Javaの(?extends T)に類似しており,TとTのサブクラスでTを置き換えることができ,リッジ置換の原則である.
    2.変わらない
    Tの子や親はサポートされず、T自体のみがサポートされていることを知っています.
    3.インバータ
    [-T],contravariant,類似(?supers T)はTの親でしかTを置き換えることができない.逆里氏置換の原則です.
    Scala上界(<:)と下界(>:)
    1) U >: T
    これはタイプ下限の定義であり,すなわちUはタイプTの親でなければならない(あるいはそれ自体,自分も自分の親とみなすことができる).
    2) S <: T
    これはタイプ上界の定義であり,すなわちSはタイプTのサブクラス(あるいはそれ自体,自分も自分のサブクラスと考えることができる)でなければならない.
    上界と下界をどのように使うか、総合的な例を見てみましょう.
    package com.usoft2
    
    //    
    class Publication(val title: String)
    
    //   
    class Book(title: String) extends Publication(title)
    
    //    
    object Library {
      //           
      val books: Set[Book] = Set(
        new Book("Programming in Scala"),
        new Book("Walden")
      )
    
      //        ,            
      def printBookList(info: Book => AnyRef) {
        //  Scala            Function1     
        assert(info.isInstanceOf[Function1[_, _]])
        //  
        for (book <- books)
          println(info(book))
      }
    
      //        ,       GetInfoAction        
      def printBokkListByTrait[P >: Book, R <: AnyRef](action: GetInfoAction[P, R]) {
        //  
        for (book <- books)
          println(action(book))
      }
    
    }
    
    //        ,P          Book,R          AnyRef
    trait GetInfoAction[P >: Book, R <: AnyRef] {
      //           ,  ()   
      def apply(book: P): R
    }
    
    //    ,      
    object Customer extends App {
      //            
      def getTitle(p: Publication): String = p.title
    
      //       
      Library.printBookList(getTitle)
    
      //    GetInfoAction      
      Library.printBokkListByTrait(new GetInfoAction[Publication, String] {
        def apply(p: Publication): String = p.title
      })
    }

    参照先:http://hongjiang.info/scala-covariance-and-contravariance/
    http://fineqtbull.iteye.com/blog/477994
    http://deltamaster.is-programmer.com/posts/48772.html?utm_source=tuicool
    ================END================