Scala: Sample classes, pattern matching, Options, partial functions, generics

Course objectives

  • Master the use of sample classes
  • Master the use of pattern matching

1. The sample class

A sample class is a special class that can be used to quickly define a class for storing data (similar to Java POJO classes), and will be used frequently in future studies of concurrent programming and frameworks like Spark and Flink.

1.1 Defining sample classes

Syntax format

case class The sample class name([var/val] Name of member variable 1: type 1; name of member variable 2: type 2; name of member variable 3: type 3)
Copy the code
  • If you want to implement a member variable that can be modified, you can add var
  • The default is val, which can be omitted

1.2 Define a sample class

demand

  • Define a Person sample class with name and age member variables
  • Create an object instance of the sample class (” Zhang SAN “, 20) and print it

Reference code

object _01CaseClassDemo {
  case class Person(name:String, age:Int)

  def main(args: Array[String) :Unit = {
    val zhangsan = Person("Zhang".20)

    println(zhangsan)
  }
}
Copy the code

1.3 Variable member variables

demand

  • Define a Person sample class with name and age member variables
  • Create an object instance of the sample class (” Zhang SAN “, 20)
  • Change Zhang SAN’s age to 23 and print

Reference code

object _02CaseClassDemo {
  case class Person(var name:String, var age:Int)

  def main(args: Array[String) :Unit = {
    val zhangsan = Person("Zhang".20)

    zhangsan.age = 23

    println(zhangsan)
  }
}
Copy the code

1.4 Methods of the sample class

When we define a sample class, the compiler automatically helps us implement the following useful methods:

  • The apply method
  • The toString method
  • The equals method
  • HashCode methods
  • The copy method

1.4.1 the apply method

The Apply method lets you quickly create objects using class names. Refer to the following code:

case class CasePerson(name:String, age:Int)

object CaseClassDemo {
  def main(args: Array[String) :Unit = {
    val lisi = CasePerson("Bill".21)
    println(lisi.toString)
  }
}
Copy the code

1.4.2 toString method

ToString Returns sample class name (member variable 1, member variable 2, member variable 3….) , we can look more closely at the members of the sample class

case class CasePerson(name:String, age:Int)

object CaseClassDemo {
  def main(args: Array[String) :Unit = {
    val lisi = CasePerson("Bill".21)
    println(lisi.toString)
    // Output: CasePerson(Li Si,21)}}Copy the code

1.4.3 equals method

The sample class automatically implements the equals method, which can be used directly to compare whether two sample classes are equal, that is, whether all member variables are equal

The sample

  • Create a sample class Person containing the name and age
  • Create two objects with names and ages “Li Si “, 21
  • Compare them to see if they’re equal
val lisi1 = CasePerson("Bill".21)
val lisi2 = CasePerson("Bill".21)
println(lisi1 == lisi2)
// Output: true
Copy the code

1.4.4 hashCode methods

The sample class automatically implements the hashCode method, which has the same hash value if all member variables have the same value, and a different hash value if one of them is different.

The sample

  • Create objects with names and ages of “Li Si “, 21
  • Create an object whose name is “Li Si” and age is 22
  • Print the hash values of the two objects separately
val lisi1 = CasePerson("Bill".21)
val lisi2 = CasePerson("Bill".22)

println(lisi1.hashCode())
println(lisi2.hashCode())
Copy the code

1.4.5 copy method

The sample class implements the copy method to quickly create an identical instance object that can be reassigned to members using named arguments

The sample

  • Create objects with names and ages of “Li Si “, 21
  • Copy the object named “King five” by copy
val lisi1 = CasePerson("Bill".21)

val wangwu = lisi1.copy(name="Fifty")
println(wangwu)
Copy the code

2. Sample objects

It is mainly used in two places:

  1. Define the enumeration
  2. As message passing without any parameters (Akka programming will cover this later)

2.1 define

Use case Object to create sample objects. The sample object is singleton, and it has no primary constructor

Syntax format

case object Name of the sample object
Copy the code

3. Pattern matching

Scala has a very powerful pattern matching mechanism that can be used in many scenarios:

  • A switch statement
  • Types of queries
  • Use pattern matching to get data quickly

3.1 Simple Pattern Matching

In Java, there is the switch keyword, which simplifies if conditional statements. In Scala, match expressions can be used instead.

Syntax format

variablematch {
    case "Constant 1"= > expression1
    case "Constant 2"= > expression2
    case "Constant 3"= > expression3
    case_ => expression4		/ / by default
}
Copy the code

The sample

specifications

  1. Enter a word from the console (using the stdin.readline method)
  2. Determines whether the word matches the following words, and returns a sentence if it does
  3. Print this sentence
The word return
hadoop Big data distributed storage and computing framework
zookeeper Big data distributed coordination service framework
spark Big data distributed memory computing framework
Did not match Did not match

Reference code

println("Please output one word:")
Stdin. readLine reads a line of text from the console
val name = StdIn.readLine()

val result = name match {
    case "hadoop"= >"Big Data Distributed Storage and Computing Framework"
    case "zookeeper"= >"Big Data Distributed Coordination Service Framework"
    case "spark"= >"Big Data Distributed Memory Computing Framework"
    case_ = >"No match"
}

println(result)
Copy the code

3.2 Matching Types

In addition to matching data like the Switch in Java, match expressions can also match types. If we want to perform different logic based on different data types, we can also use match expressions to do so.

define

Syntax format

variablematch {
    casetype1Variable name: type1= > expression1
    casetype2Variable name: type2= > expression2
    casetype3Variable name: type3= > expression3.case_ => expression4
}
Copy the code

The sample

specifications

  • Define a variable as type Any, and assign the values “hadoop”, 1, and 1.0, respectively
  • Define a pattern match, and then print the name of the type separately

Reference code

val a:Any = "hadoop"

val result = a match {
    case_ :String= >"String"
    case_ :Int= >"Int"
    case_ :Double= >"Double"
}

println(result)
Copy the code

[!NOTE]

If matching variables are not required in a case expression, you can use underscores instead

3.3 the guards

In Java, you can simply add multiple case tags; for example, to match 0-7, you need to write out eight case statements. Such as:

int a = 0;
switch(a) {
    case 0: a += 1;
    case 1: a += 1;
    case 2: a += 1;
    case 3: a += 1;
    case 4: a += 2;
    case 5: a += 2;
    case 6: a += 2;
    case 7: a += 2;
    default: a = 0;
}
Copy the code

In Scala, you can simplify this code by using guards — that is, adding if conditions to case statements.

The sample

specifications

  • Read a number a from the console (using stdin.readint)
  • If a >= 0 and a <= 3, print [0-3]
  • If a >= 4 and a <= 8, print [3,8]
  • Otherwise, the print does not match

Reference code

val a = StdIn.readInt()

a match {
    case _ if a >= 0 && a <= 3 => println("[0, 3]")
    case _ if a >= 4 && a <= 8 => println("[3-8]")
    case _ => println("No match")}Copy the code

3.4 Matching Example Classes

Scala uses pattern matching to match sample classes so that member data can be sampled quickly. We’ll use it later when we develop the Akka case.

The sample

specifications

  • Create two sample classes Customer and Order
    • Customer contains the name and age fields
    • Order contains the ID field
  • Define objects for each of the two case classes and specify them as Any
  • Match the two objects with a pattern and print their member variable values separately

Reference code

Create two sample classes
case class Person(name:String, age:Int)
case class Order(id:String)

def main(args: Array[String) :Unit = {
    // 2. Create an example class object and assign it to type Any
    val zhangsan:Any = Person("Zhang".20)
    val order1:Any = Order("001")

    // 3. Use match... Case expression for pattern matching
    // Get the member variables of the sample class
    order1 match {
        case Person(name, age) => println(S "name:${name}Age:${age}")
        case Order(id1) => println(S "ID is:${id1}")
        case _ => println("No match")}}Copy the code

3.5 Matching Set

Pattern matching in Scala can also be used to match collections.

3.6 Matching Arrays

example

  • Modify the code in turn to define the following three arrays

    Array(1,x,y)   // Start with 1, the next two elements are not fixed
    Array(0)	   // An element that matches only one 0 element
    Array(0,...).// Can be any number, but starts with 0
    Copy the code
  • Use the pattern to match the above array

Reference code

val arr = Array(1.3.5)
arr match {
    case Array(1, x, y) => println(x + "" + y)
    case Array(0) => println("only 0")
    case Array(0, _*) => println("Zero...")
    case _ => println("something else")}Copy the code

3.7 Matching List

example

  • Modify the code in turn to define the following three lists

    List(0)				// Save a list of 0 elements only
    List(0,...).// Lists that start with 0
    List(x,y)	   		// A list of only two elements
    Copy the code
  • Use patterns to match the list above

Reference code

val list = List(0.1.2)

list match {
    case 0: :Nil => println("List of 0's only")
    case 0 :: tail => println("List starting with 0")
    case x :: y :: Nil => println(S "has only two other elements${x}.${y}The list of")
    case _ => println("No match")}Copy the code

3.8 Matching Tuples

example

  • Modify the code in turn to define the following two tuples

    (1, x, y)		// A tuple of three elements starting with 1
    (x, y, 5)   // There are three elements, the last of which is a tuple of 5
    Copy the code
  • Match the above elements with a pattern

Reference code

val tuple = (2.2.5)

tuple match {
    case (1, x, y) => println(S "three elements, tuple beginning with 1:1,${x}.${y}")
    case (x, y, 5) => println(S "three elements, tuple ending in 5:${x}.${y}, 5")
    case _ => println("No match")}Copy the code

3.9 Pattern matching in variable declarations

When defining variables, you can use pattern matching to get data quickly

3.9.1 sample | for the elements in the array

specifications

  • Generates an array of 0-10 numbers, using pattern matching to get the second, third, and fourth elements, respectively

Reference code

val array = (1 to 10).toArray
val Array(_, x, y, z, _*) = array

println(x, y, z)
Copy the code

3.9.2 sample | retrieve data from the List

specifications

  • Generate a list of 0-10 numbers, using pattern matching to get the first and second elements, respectively

Reference code

val list = (1 to 10).toList
val x :: y :: tail = list

println(x, y)
Copy the code

4. The type of Option

The Option type can be used to effectively avoid null reference exceptions. That is, when we return some data in the future, we can return an Option type instead.

define

In Scala, the Option type is used to represent optional values. This type of data comes in two forms:

  • Some(x) : represents the actual value

  • None: indicates that there is no value

  • Using the getOrElse method, a default value can be specified when the value is None

The sample a

example

  • Define a method that divides two numbers using the Option type to encapsulate the result
  • Pattern matching is then used to print the results
    • Instead of dividing by zero, print the result
    • Division by zero print exception error

Reference code

  /** * define the division operation * @param a parameter 1 * @param b parameter 2 * @return Option wrap Double type */
  def dvi(a:Double, b:Double) :Option[Double] = {
    if(b ! =0) {
      Some(a / b)
    }
    else {
      None}}def main(args: Array[String) :Unit = {
    val result1 = dvi(1.0.5)

    result1 match {
      case Some(x) => println(x)
      case None => println("Division by zero exception")}}Copy the code

Example 2

example

  • Rewrite the above case, using the getOrElse method, when dividing by zero, or the default is 0

Reference code

def dvi(a:Double, b:Double) = {
    if(b ! =0) {
        Some(a / b)
    }
    else {
        None}}def main(args: Array[String) :Unit = {
    val result = dvi(1.0).getOrElse(0)

    println(result)
}
Copy the code

5. Partial functions

Partial functions can provide a concise syntax that simplifies the definition of functions. Functional programming with collections can make your code more elegant.

define

  • A set of case statements without match wrapped in curly braces is a partial function

  • Partial functions are an instance of PartialFunction[A, B]

    • A indicates the input parameter type
    • B represents the return result type

The sample a

example

Define a partial function that returns as follows

The input The return value
1 one
2 two
3 three
other other

Reference code

Func1 is a partial function whose input parameter is Int and return a String
val func1: PartialFunction[Int.String] = {
    case 1= >"一"
    case 2= >"二"
    case 3= >"Three"
    case_ = >"Other"
}

println(func1(2))
Copy the code

Example 2

example

  • Define a list of numbers 1-10

  • Please convert all numbers 1-3 to [1-3].

  • Please convert all numbers 4-8 to [4-8].

  • Convert other numbers to (8-*)

Reference code

val list = (1 to 10).toList

val list2 = list.map{
    case x if x >= 1 && x <= 3= >"[1-3]"
    case x if x >= 4 && x <= 8= >"[4-8]"
    case x if x > 8= >"(8 - *]"
}

println(list2)
Copy the code

6. Regular expressions

In Scala, it’s easy to use regular expressions to match data.

define

The Regex class

  • Scala provides the Regex class to define regular expressions

  • To construct a RegEx object, use the r method of the String class

  • It is recommended to use three double quotes to represent the regular expression, otherwise you have to escape the backslash in the regular

    val regEx = """ regular expression """.r
    Copy the code

FindAllMatchIn method

  • The findAllMatchIn method is used to get all the strings matched by the re

The sample a

example

Reference code

val r = "" ". + @. + \.. "+" ".r

val eml1 = "[email protected]"
val eml2 = "[email protected]"

if(r.findAllMatchIn(eml1).size > 0) {
    println(eml1 + "Email is legal")}else {
    println(eml1 + "Email is not valid.")}if(r.findAllMatchIn(eml2).size > 0) {
    println(eml2 + "Email is legal")}else {
    println(eml2 + "Email is not valid.")}Copy the code

Example 2

example

Find all illegal mailboxes in the following list

"[email protected]", "[email protected]", "[email protected]", "123afadff.com"
Copy the code

Reference code

val emlList =
List("[email protected]"."[email protected]"."[email protected]"."123afadff.com")

val regex = "" ". + @. + \.. "+" ".r

val invalidEmlList = emlList.filter {
    x =>
    if (regex.findAllMatchIn(x).size < 1) true else false
}

println(invalidEmlList)
Copy the code

Example 3

example

  • The following email list is available

    "[email protected]"."[email protected]"."[email protected]"."123afadff.com"
    Copy the code
  • The regular expression is used to match the mailbox operator’s name. For example, if the email address is [email protected], 163 needs to be matched

    • Use parentheses to match groups
  • Print the matched email address and carrier

Reference code

// Use parentheses to indicate a grouping
val regex = "Is" ". + @ \. (. +). "+" ".r

val emlList =
List("[email protected]"."[email protected]"."[email protected]"."123afadff.com")

val emlCmpList = emlList.map {
    case x@regex(company) => s"${x}= >${company}"
    case x => x + Unknown "= >"
}

println(emlCmpList)
Copy the code

7. Exception handling

Take a look at the following code.

  def main(args: Array[String) :Unit = {
   val i = 10 / 0
    
    println("Hello!)}Exception in thread "main" java.lang.ArithmeticException: / by zero
	at ForDemo$.main(ForDemo.scala:3)
	at ForDemo.main(ForDemo.scala)
Copy the code

When you execute the program, you can see that Scala throws an exception and does not print “hello.” It means the program was aborted after an error.

So how to solve this problem?

In Scala, exception handling can be used to solve this problem

7.1 Catching Exceptions

Syntax format

try {
    / / code
}
catch {
    caseEx: indicates the exception type1= >/ / code
    caseEx: indicates the exception type2= >/ / code
}
finally {
    / / code
}
Copy the code
  • The code in try is the business processing code we wrote
  • In catch, the code that needs to be executed when an exception occurs
  • In finally, is code that executes whether or not an exception occurs

The sample

example

  • Use the try.. Catch to catch a division by zero exception

Reference code

try {
    val i = 10 / 0

    println("Hello!)}catch {
    case ex: Exception => println(ex.getMessage)
} 
Copy the code

7.2 Throwing an Exception

We can also throw an exception in a method. The syntax is similar to Java, using throw new Exception…

An exception is thrown

example

  • Throw an exception in the main method

Reference code

  def main(args: Array[String) :Unit = {
    throw new Exception("This is an anomaly")}Exception in thread "main" java.lang.Exception: This is an exception atForDemo$.main(ForDemo.scala:3)
	at ForDemo.main(ForDemo.scala)
Copy the code
  • Scala does not need to declare the exception to be thrown on a method, and it has solved the problem of checking exceptions that are considered design failures in Java.

Here is the Java code

public static void main(String[] args) throws Exception {
    throw new Exception("This is an anomaly");
}
Copy the code

8. Extractor

We’ve already used scala’s very powerful pattern matching feature, which allows us to quickly match member variables in our sample classes. Such as:

Create two sample classes
case class Person(name:String, age:Int)
case class Order(id:String)

def main(args: Array[String) :Unit = {
    // 2. Create an example class object and assign it to type Any
    val zhangsan:Any = Person("Zhang".20)
    val order1:Any = Order("001")

    // 3. Use match... Case expression for pattern matching
    // Get the member variables of the sample class
    order1 match {
        case Person(name, age) => println(S "name:${name}Age:${age}")
        case Order(id1) => println(S "ID is:${id1}")
        case _ => println("No match")}}Copy the code

Can all classes do this pattern matching? The answer is:

You can’t. To support pattern matching, you must implement an extractor.

[!NOTE]

The sample class automatically implements apply and unapply methods

8.1 Define extractor

We learned earlier that the apply method in a companion object of a class can be used to quickly build an object using the class name. There is also an unapply method in the companion object. Unapply, as opposed to apply, breaks down objects of that class into elements.

To implement an extractor for a class, simply implement an unapply method on the companion object of the class.

Syntax format

def unapply(stu:Student) :Option[(type1Type,2Type,3...) ] = {if(stu ! =null) {
        Some((variable1, the variable2, the variable3...))
    }
    else {
        None}}Copy the code

The sample

example

  • Create a Student class with the name and age fields
  • Implement a class destructor and use match expressions for pattern matching to extract fields from the class.

Reference code

class Student(var name:String, var age:Int)

object Student {
    def apply(name:String, age:Int) = {
        new Student(name, age)
    }

    def unapply(student:Student) = {
        val tuple = (student.name, student.age)

        Some(tuple)
    }
}

def main(args: Array[String) :Unit = {
    val zhangsan = Student("Zhang".20)

    zhangsan match {
        case Student(name, age) => println(s"${name}= >${age}")}}Copy the code

9. The generic

Scala, like Java, has generics support for classes and attributes and methods. When we look at sets, we’re going to talk about generics.

scala> val list1:List[String] = List("1"."2"."3")
list1: List[String] = List(1.2.3)
Copy the code

So how do you define generics yourself?

9.1 Define a generic method

In Scala, type parameters are defined using square brackets.

Syntax format

def The method name[generic name](..) = {/ /...
}
Copy the code

The sample

example

  • A method to get the middle element of an array of any type
    • Implementation without generics (Array[Int] implementation)
    • Add generic support

Reference code

The implementation of generics is not considered

  def getMiddle(arr:Array[Int]) = arr(arr.length / 2)

  def main(args: Array[String) :Unit = {
    val arr1 = Array(1.2.3.4.5)

    println(getMiddle(arr1))
  }
Copy the code

Add generic support

def getMiddleElement[T](array:Array[T]) =
array(array.length / 2)

def main(args: Array[String) :Unit = {
    println(getMiddleElement(Array(1.2.3.4.5)))
    println(getMiddleElement(Array("a"."b"."c"."d"."e")))}Copy the code

9.2 a generic class

Scala classes can also define generics. Next, let’s learn how to define Scala generic classes

define

Syntax format

class class[T] (Val variable name:T)
Copy the code
  • Define a generic class, directly following the class name with square brackets, specifying the generic parameters to use
  • Once you specify the generic parameters for the class, you use those type parameters to define variables

The sample

example

  • Implement a Pair generic class
  • The Pair class contains two fields, and the types of the fields are not fixed
  • Create generic class objects of different types and print them

Reference code

case class Pair[T] (var a:T, var b:T)

def main(args: Array[String) :Unit = {
    val pairList = List(
        Pair("Hadoop"."Storm"),
        Pair("Hadoop".2008),
        Pair(1.0.2.0),
        Pair("Hadoop".Some(1.9))
    )

    println(pairList)
}
Copy the code

The upper and lower bounds of 9.3

Requirements:

When we define generics for methods/classes, we define which class must inherit from, or be the parent of, which class. At this point, you need to use upper and lower bounds.

9.3.1 Upper bound definition

Using the <: type name adds an upper bound to the type, indicating that the generic parameter must inherit from the class (or itself)

Syntax format

[T< : type]Copy the code

The sample

example

  • Define a Person class
  • Define a Student class that inherits from the Person class
  • Define a Demo generic method that takes an Array parameter,
  • Restrict the Array element type of the Demo method to Person or a subclass of Person
  • The test calls demo, passing in arrays of different element types

Reference code

class Person
class Student extends Person

def demo[T< :Person](a:Array[T]) = println(a)

def main(args: Array[String) :Unit = {
    demo(Array(new Person))
    demo(Array(new Student))
    // error compiling, must be subclass of Person
    // demo(Array("hadoop"))
}
Copy the code

9.3.2 lower bound

The upper bound is that it must be a subclass of a class or must inherit from a class, and the lower bound is that it must be a parent (or parent) of a class

Syntax format

[T> : type]Copy the code

[!NOTE]

If the class has both upper and lower bounds. The lower bound is in front, the upper bound is in back

The sample

example

  • Define a Person class
  • Define a Policeman class that inherits from the Person class
  • Define a Superman class that inherits from Policeman
  • Define a Demo generic method that takes an Array parameter,
  • Restrict the Array element types of the Demo method to Person, Policeman
  • The test calls demo, passing in arrays of different element types

Reference code

class Person
class Policeman extends Person
class Superman extends Policeman

def demo[T> :Policeman](array:Array[T]) = println(array)

def main(args: Array[String) :Unit = {
    demo(Array(new Person))
    demo(Array(new Policeman))
    Superman is a subclass of Policeman
    // demo(Array(new Superman))
}
Copy the code

9.4 Covariant, contravariant and non-variant

The Spark source code uses a lot of covariant, contravariant, and non-variant. Learning this knowledge will be helpful for us to read the Spark source code in the future.

Let’s look at a conversion problem:

class Pair[T]

object Pair {
  def main(args: Array[String) :Unit = {
    val p1 = Pair("hello")
    // failed to convert P1 to P2
    val p2:Pair[AnyRef] = p1

    println(p2)
  }
}
Copy the code

How do you make a class with generics support type conversions?

9.4.1 change

Syntax format

class Pair[T]{}
Copy the code
  • The default generic class is non-mutable
  • Type B is A subtype of A. Pair[A] and Pair[B] have no dependencies
  • Java is the same

9.4.2 covariance

Syntax format

class Pair[+T]
Copy the code
  • Type B is A subtype of A. Pair[B] can be considered A subtype of Pair[A]
  • The direction of a parameterized type is the same as the direction of the type.

9.4.3 inverter

Syntax format

class Pair[-T]
Copy the code
  • Type B is A subtype of A, and Pair[A] can conversely be considered A subtype of Pair[B]
  • The direction of a parameterized type is opposite to the direction of the type

The sample

example

  • Define a Super class and a Sub class that inherits from the Super class
  • Three generic classes are defined using covariant, contravariant, and nonvariant classes
  • Create generic classes separately to demonstrate covariant, contravariant, and nonvariant

Reference code

class Super
class Sub extends Super

class Temp1[T]
class Temp2[+T]
class Temp3[-T]

def main(args: Array[String) :Unit = {
    val a:Temp1[Sub] = new Temp1[Sub]
    // Compile error
    / / the change
    //val b:Temp1[Super] = a

    / / covariant
    val c: Temp2[Sub] = new Temp2[Sub]
    val d: Temp2[Super] = c

    / / inverter
    val e: Temp3[Super] = new Temp3[Super]
    val f: Temp3[Sub] = e
}
Copy the code

Using covariant, contravariant, and non-variant, learning this knowledge will help us read the Spark source code in the future.

Let’s look at a conversion problem:

class Pair[T]

object Pair {
  def main(args: Array[String) :Unit = {
    val p1 = Pair("hello")
    // failed to convert P1 to P2
    val p2:Pair[AnyRef] = p1

    println(p2)
  }
}
Copy the code

How do you make a class with generics support type conversions?

9.4.1 change

Syntax format

class Pair[T]{}
Copy the code
  • The default generic class is non-mutable
  • Type B is A subtype of A. Pair[A] and Pair[B] have no dependencies
  • Java is the same

imG-MD4Q8pQT-1625207288225

9.4.2 covariance

Syntax format

class Pair[+T]
Copy the code
  • Type B is A subtype of A. Pair[B] can be considered A subtype of Pair[A]
  • The direction of a parameterized type is the same as the direction of the type.

9.4.3 inverter

Syntax format

class Pair[-T]
Copy the code
  • Type B is A subtype of A, and Pair[A] can conversely be considered A subtype of Pair[B]
  • The direction of a parameterized type is opposite to the direction of the type

The sample

example

  • Define a Super class and a Sub class that inherits from the Super class
  • Three generic classes are defined using covariant, contravariant, and nonvariant classes
  • Create generic classes separately to demonstrate covariant, contravariant, and nonvariant

Reference code

class Super
class Sub extends Super

class Temp1[T]
class Temp2[+T]
class Temp3[-T]

def main(args: Array[String) :Unit = {
    val a:Temp1[Sub] = new Temp1[Sub]
    // Compile error
    / / the change
    //val b:Temp1[Super] = a

    / / covariant
    val c: Temp2[Sub] = new Temp2[Sub]
    val d: Temp2[Super] = c

    / / inverter
    val e: Temp3[Super] = new Temp3[Super]
    val f: Temp3[Sub] = e
}
Copy the code