当前位置:网站首页>Covariance, inversion, upper bound, lower bound, etc. in Scala

Covariance, inversion, upper bound, lower bound, etc. in Scala

2021-10-22 10:58:09 Bird's nest

Catalog [−]

  1. Java The covariance and contravariant in
  2. Scala The covariance of
  3. Scala The inverse of
  4. Lower bound lower bounds
  5. upper bound upper bounds
  6. Comprehensive covariance , Inversion , upper bound , Lower bound
  7. View Bound <%
  8. Context Bound
  9. Reference documents

Scala Covariant inversion and Java The covariant inversion in is different , It looks more complicated . This paper deals with Scala Make a summary of these concepts in .
First, let's look at some concepts :

  • covariant Covariance . Enables you to use subclasses of types larger than originally specified
  • Contravariance Inversion . Enables you to use a parent class of a type larger than the originally specified type .
  • Invariance unchanged . You can only use the original specified type , No covariance or inversion
  • Upper bounds upper bound .
  • Lower bounds Lower bound .

Java The covariance and contravariant in

First of all, let's review Java The covariance and contravariant in , It makes it easier for us to understand Scala The covariance and contravariant in .
1 Covariance

       
       
       
1
2
3
4
5
6
       
       
       
class Super {
Object getSomething(){}
}
class Sub extends Super {
String getSomething() {}
}

Sub.getSomething() Is a covariant type , Because its return type is Super.getSomething Subclass of return type .

2 Inversion

       
       
       
1
2
3
4
5
6
       
       
       
class Super{
void doSomething(String parameter)
}
class Sub extends Super{
void doSomething(Object parameter)
}

Sub.getSomething() Is an inverse type , Because its input parameter is Super.getSomething The parent class of the input parameter .

3 Generic
There are also covariants and inversions in generics .

       
       
       
1
2
3
4
5
6
7
8
9
10
       
       
       
List<String> aList...
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);

You can call covariantList All methods that do not require generic parameters , Because generic parameters must extends Object, But you don't know its exact type at compile time . But you can call getter Method , Because the return type always matches Object type .
contravariantList Just the opposite , You can call all methods with generic parameters , Because you can definitely pass in a String Parent class of . however getter The method doesn't work .

Scala The covariance of

The first thing we need to know is subtypes (subtyping). A class can be a subclass of other classes (sub-) Or the parent class (super-). We can use mathematical concepts (partial order) To define :

       
       
       
1
       
       
       
A -> B iff A <: B

When we define a covariant type List[A+] when ,List[Child] It can be List[Parent] Subtypes of .
When we define an inverse type List[-A] when ,List[Child] It can be List[Parent] The father type of .

See the following example :

       
       
       
1
2
3
4
5
6
7
8
9
10
       
       
       
class Animal {}
class Bird extends Animal {}
class Consumer[T](t: T) {
}
class Test extends App {
val c:Consumer[Bird] = new Consumer[Bird]( new Bird)
val c2:Consumer[Animal] = c
}

c Cannot assign a value to c2, because Consumer Defined as an invariant type .

A little change :

       
       
       
1
2
3
4
5
6
7
8
9
10
       
       
       
class Animal {}
class Bird extends Animal {}
class Consumer[+T](t: T) {
}
class Test extends App {
val c:Consumer[Bird] = new Consumer[Bird]( new Bird)
val c2:Consumer[Animal] = c
}

because Consumer Defined as covariant , therefore Consumer[Bird] yes Consumer[Animal] Subtypes of , So it can be assigned to c2.

Scala The inverse of

Change the above example :

       
       
       
1
2
3
4
5
6
7
8
9
10
       
       
       
class Animal {}
class Bird extends Animal {}
class Consumer[-T](t: T) {
}
class Test extends App {
val c:Consumer[Bird] = new Consumer[Bird]( new Bird)
val c2:Consumer[Hummingbird] = c
}

here Consumer[-T] Defined as an inverse type , therefore Consumer[Bird] Regarded as Consumer[Hummingbird] Subtypes of , so c Can be assigned to c2.

Lower bound lower bounds

If the covariant class contains methods with type parameters :

       
       
       
1
2
3
       
       
       
class Consumer[+T](t: T) {
def use(t: T) = {}
}

Compilation error . The error message is "Covariant type T occurs in contravariant position in type T of value t".
However, if the returned result is a type parameter, there is no problem .

       
       
       
1
2
3
       
       
       
class Consumer[+T](t: T)(implicit m1:Manifest[T]) {
def get(): T = {m1.runtimeClass.newInstance.asInstanceOf[T]}
}

To use type parameters in method parameters , You need to define the lower bound :

       
       
       
1
2
3
       
       
       
class Consumer[+T](t: T) {
def use[U >: T](u : U) = {println(u)}
}

upper bound upper bounds

Examples of using upper bounds in inverse classes :

       
       
       
1
2
3
       
       
       
class Consumer[-T](t: T) {
def get[U <: T]()(implicit m1:Manifest[U]): U = {m1.runtimeClass.newInstance.asInstanceOf[U]}
}

You can see that the return value of the method is of covariant type , The parameter of the method is the inverse type .
Therefore, the type parameter of covariant class can be used in the type of return value of method , A lower bound must be used on the parameter type of the method >:.
The type parameter of the inverse class can be used on the parameter type of the method , When used as the return value type of a method, you must use the upper bound binding <:.

Comprehensive covariance , Inversion , upper bound , Lower bound

A comprehensive example :

       
       
       
1
2
3
4
5
6
7
8
9
10
11
12
       
       
       
class Animal {}
class Bird extends Animal {}
class Consumer[-S,+T]()(implicit m1:Manifest[T]) {
def m1[U >: T](u: U): T = {m1.runtimeClass.newInstance.asInstanceOf[T]} // Covariance , Lower bound
def m2[U <: S](s: S)(implicit m2:Manifest[U]): U = {m1.runtimeClass.newInstance.asInstanceOf[U]} // Inversion , upper bound
}
class Test extends App {
val c:Consumer[Animal,Bird] = new Consumer[Animal,Bird]()
val c2:Consumer[Bird,Animal] = c
c2.m1( new Animal)
c2.m2( new Bird)
}

View Bound <%

Scala There is also a view binding function , Such as

       
       
       
1
2
3
4
5
6
       
       
       
class Bird { def sing = {}}
class Toy {}
class Consumer[T <% Bird]() {
def use(t: T) = t.sing
}

Or type parameters on methods :

       
       
       
1
2
3
4
5
6
7
8
9
10
11
       
       
       
class Bird { def sing = {}}
class Toy {}
class Consumer() {
def use[T <% Bird](t: T) = t.sing
}
class Test extends App {
val c = new Consumer()
c.use( new Toy)
}

It requires T There must be an implicit conversion to Bird, That is to say T => Bird, Otherwise, the above code will compile incorrectly :

       
       
       
1
       
       
       
No implicit view available from Toy => Bird.

Add an implicit conversion , Compile and pass .

       
       
       
1
2
3
4
5
6
7
8
9
10
11
12
13
14
       
       
       
import scala.language.implicitConversions
class Bird { def sing = {}}
class Toy {}
class Consumer() {
def use[T <% Bird](t: T) = t.sing
}
class Test extends App {
implicit def toy2Bird(t: Toy) = new Bird
val c = new Consumer()
c.use( new Toy)
}

Context Bound

context bound stay Scala 2.8.0 Introduction in , Also known as type class pattern.
view bound Use A <% String The way ,context bound You need a parameterized type , Such as Ordered[A].
It declares a type A, Implicitly there is a type B[A], The grammar is as follows :

       
       
       
1
       
       
       
def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A]

A clearer example :

       
       
       
1
       
       
       
def f[A : ClassManifest](n: Int) = new Array[A](n)

And such as

       
       
       
1
       
       
       
def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)

Reference documents

  1. http://www.jayway.com/2011/10/03/scala-type-variances-part-one/
  2. http://www.jayway.com/2011/10/04/scala-type-variances-part-two/
  3. http://www.jayway.com/2011/10/05/scala-type-variances-part-three/
  4. http://twitter.github.io/scala_school/type-basics.html#variance
  5. http://docs.scala-lang.org/tutorials/tour/variances.html
  6. http://blog.csdn.net/oopsoom/article/details/24773239
  7. http://stackoverflow.com/questions/2501023/demonstrate-covariance-and-contravariance-in-java
  8. http://stackoverflow.com/questions/4465948/what-are-scala-context-and-view-bounds

版权声明
本文为[Bird's nest]所创,转载请带上原文链接,感谢
https://chowdera.com/2021/10/20211009000611674i.html

随机推荐