Object oriented and advanced syntax of Scala

Posted by tartou2 on Wed, 20 Oct 2021 02:05:53 +0200

1, Object oriented

1. Class and object details

(1) Class composition structure

Constructor, member variable, member method (function), local variable, code block, internal class

(2) Constructor

  • scala has two types of constructors: primary and secondary
  • The main constructor follows the class name, such as class Student2(val name: String, var age: Int). If the class name is not followed by parentheses or there are no parameters in parentheses, it is an empty parameter main constructor
  • Auxiliary constructor is a special method defined in the class def this(name:String,age:Int,gender:String)
  • In the auxiliary constructor, the first line must call another constructor
  • The parameters of the auxiliary constructor cannot be completely consistent with those of the main constructor (number of parameters, parameter type, parameter order)
  • An auxiliary constructor with null parameters can be defined, but the parameters of the main constructor must be initialized and assigned
  • When creating an instance of a class, you can call any constructor
  • Scope: the scope of the auxiliary constructor is only in the method. The scope of the main constructor is all the scope in the class except for the member attribute and member method (you can view the source code through decompilation)
  • If the parameters of the main constructor are not decorated with var/val, their parameters are local variables and can only be used in this class
  • The parameter of the constructor modified by val is the member attribute of the class, which is read-only
  • The parameter of the constructor modified by var is the member attribute of the class, but it is readable and writable

Note: the main constructor executes all statements in the class definition. It is equivalent that the whole class is in the main constructor

class User { // Class has a parameterless primary constructor by default}
val user = new User
class User3 {
   var name: String = _
   var age: Int = _
  // Auxiliary constructor
  def this(name: String, age: Int) {
    // The first line in the constructor must call the main constructor or another constructor
    this()
    this.name = name
    this.age = age
  }
  // Auxiliary constructor
  def this(msg: String) = {
    // The first line calls a construct
    this("ww", 12)
    println(msg)
  }
}
val u1 = new User3()
val u2 = new User3("")
val u3 = new User3("lisi", 23)
println(u3.name)

(3) Member variable

  • Variables decorated with val and var in the main constructor are member variables
  • Variables defined in a class are also member variables
  • The variable modified by val has only getter method to initialize by default
  • var modified variables have get and set methods by default, and are used directly for point attribute operations_ The placeholder can be assigned later
  • Using @ BeanProperty on a member variable generates the getMsg setMsg method
// 1. The variables modified with val and var in the main constructor are member variables
class Person(val address:String) {
  // 2. The variable modified by Val has only getter method to initialize by default
  val name:String = "val Modifier must be assigned here"
  // 3. Var modified variables have get and set methods by default, and are used for direct point attribute operations_ The placeholder can be assigned later
  var age:Int= _  // The placeholder can be assigned later
  // 4 @BeanProperty will generate getMsg setMsg method
  @BeanProperty  
  var msg:String =  _
}

(4) Member methods / functions

A function or method defined at the member position of a class is part of the member of the class

(5) Local variable

The parameters defined in the function / method are defined in the code block, and the variables defined in the conditional expression of process control become local variables, which can only be used in the local code block

(6) Code block

The code block in the class is executed once every time it is instantiated (created).
The code block in the associated object is a static code block, which is executed only once when the class is loaded and will not be executed after that.

(7) Associated classes and associated objects

Condition 1: in the same source file, condition 2: the object name and class name are the same

class Demo{
  val id = 1
  private var name = "xiaoqing"
  def printName(): Unit ={
    //In the Dog class, you can access the private properties of the associated object Dog
    println(Demo.CONSTANT + name )
  }
}
/**
  * Companion Object 
  */
object Demo{
  //Private properties in companion objects
  private val CONSTANT = "Woof, woof : "
  def main(args: Array[String]) {
    val p = new Demo
    //Access private field name
    p.name = "123"
    p.printName()
  }
}

Associated objects and associated classes can access each other's private properties and private methods

(8) apply method

The apply method is a special method. When calling, you only need to pass in parameters without writing the method name

class Demo1 {
//Define the apply method in the half life class of Demo1
  def apply() {
    println("This is a class")
  }
}

object Demo1 {
//Define the apply method in the half life object of Demo1
  def apply() {
    println("This is the object")
  }
}

object Demo2 {
  def main(args: Array[String]): Unit = {
    val demo = new Demo1()
    //Call the apply method in the half life class
    demo() //Output: This is a class
    //Call the apply method in the half life object
    Demo1() //Output: This is an object
  }
}

be careful:

  • The main purpose of the apply method is not to use new to get the instance object, and it is singleton by default!!!
  • The apply method is recommended to be written in the companion object
  • Do not omit () if the apply method has no parameters

(9)classOf,isInstanceOf,asInstanceOf

  • classOf[T] as T.class in Java
  • obj.isInstanceOf[T] judge whether it is an instance object of T
  • obj.asInstanceOf[T] cast

(10) , class, object, case class, case object differences

Class is similar to class in Java;
object Scala cannot define static members, instead of defining singleton objects;
case class is called sample class. It is a special class and is often used for pattern matching.

1, Relationship between class and object:

  • 1. The singleton object cannot take parameters, and the class can
  • 2. When an object can have the same name as a class, object is called an associated object and class is called an associated class;
  • 3. Classes and associated objects can access their private properties, but they must be in a source file;
  • 4. The class will only be compiled and will not be executed. To execute, it must be in Object.

2, Difference between case class and class:

  • 1.case class can be initialized without or with new, but ordinary classes must be added with new;
  • 2.case class implements the equals and hashCode methods by default;
  • 3.case class can be serialized by default, and Serializable is implemented;
  • 4.case class automatically inherits some functions from scala.Product;
  • 5. The case class constructor parameter is public and can be accessed directly;
  • 6.case class attribute values cannot be modified by default;
  • 7. The most important function of case class is to support pattern matching, which is also an important reason for defining case class.

3, Difference between case class and case object:

  • 1. There are parameters and no parameters in the class. When the class has parameters, use case class. When the class has no parameters, use case object.

4, When a class is called case class, scala will help us do the following things:

  • 1. If the parameter in the constructor is not declared as var, it is of type val by default, but it is generally not recommended to declare the parameter in the constructor as var
  • 2. Automatically create the associated object and implement the sub apply method in it, so that we can use the new object without directly displaying it
  • 3. The associated objects will also help us implement the unapply method, so that we can apply the case class to pattern matching. We will focus on the unapply method in the "extractor" section later
  • 4. Implement your own toString, hashCode, copy and equals methods
  • In addition, case class is no different from other ordinary scala classes

2. Permission modifier

The permission modifiers in scala include public, protected and private. By default, public is used and can be accessed in any range

The attributes and methods modified by protected are valid in the class and its associated objects, valid in the associated objects of subclasses and their subclasses, and invalid elsewhere.

The access scope of the property or method modified by private is in this class and associated objects

scala has more strict access control

  • private [this] scope is in the current class, and the associated object cannot be accessed
  • private [packageName] specifies that the package and its sub packages are valid. The writing method of package name is to write the registration directly without hierarchical path. Note: you cannot write the package name at the same level as the current package. You can only write the current package or the parent package and grandfather package of the current package

It is common for classes, main constructors, auxiliary constructors, properties in main constructors, properties in classes, and methods in classes

3. Idiosyncrasy

(1) Trait use

Trait is equivalent to the interface of java. More powerful than the interface.

Attributes, abstract methods and ordinary methods (Methods with concrete implementation) can be defined in attributes. The implementation class needs to implement all abstract methods. It will inherit the properties and ordinary methods in the characteristics and can be called directly.

Scala's classes can only inherit a single parent class, but can implement (inherit, mix in) multiple traits

There are no implements in scala, and extensions and with are used to implement multiple attributes
with can only be followed by traits

Note that attributes cannot have a primary constructor

//Trait 1
trait T1 {
  val name:String = "I am a trait"
  def add(x:Int)
  def show(desc:String)={
    println(desc)
  }
}
// Trait 2
trait  T2{
  def show()
}
// Realize multiple characteristics  
class Demo extends  T1 with T2{
  // Implementing abstract methods in interfaces
  override def add(x:Int): Unit = {
  }
  // The override keyword for implementing abstract methods can be omitted
  def show(): Unit = {}
}

(2) Dynamic blending of traits

Dynamic blending: inherit the characteristics and implement the corresponding methods when creating the instance object.
Use with traits to blend in traits when creating objects
You can mix multiple traits with trait 1 and trait 2 to rewrite the abstract method of all traits

class A {
}
class B{
  def  haha={
    // Dynamic mixing implements the specific interface and rewrites the corresponding abstract method when it is used
    val a = new A with T1 with T2 {
      override def add(x: Int): Unit = ???
      override def show(): Unit = ???
    }
  }
}

4. Abstract class

In Scala, classes decorated with abstract are called abstract classes.
Attributes, unimplemented methods (abstract methods) and concretely implemented methods can be defined in abstract classes.
Abstract classes can have no abstract methods. Classes with abstract methods must be abstract classes
Class can only inherit one abstract class, and with can only be followed by traits

//The class modified by abstract is an abstract class
abstract class Animal {
  println("Animal's constructor ....")
  // Define a name attribute
  val name: String = "animal"
  // There is no way to implement it
  def sleep()
  // Method with specific implementation
  def eat(f: String): Unit = {
    println(s"$f")
  }}

5. Sample class

The class decorated with case is the sample class

  • The parameters in the constructor are val decorated by default
  • The sample class will automatically create the companion object, and implement the apply and unapply methods in it. new is not used when creating the object
  • Good support for matching patterns
  • By default, it implements its own toString, hashcode, copy and equals methods
// Define a sample class
case class Person(name:String , var age:Int) {
}

Some differences between case class and class:

  • When initializing a case class, you do not need new, but when initializing an ordinary class, you must use new.
  • case class overrides the toString method. equals and hashCode are implemented by default
  • case class implements the serialization interface with Serializable
  • Case classes support pattern matching (the most important feature). All case class es must have a parameter list
  • Use case class for parameters and case object for none
  • case class and case object can be passed as messages

2, Advanced Grammar

1. Higher order function

  • The parameter of a function is the function or method of the function
  • The return value of a function is the function or method of the function
  • The parameters and return values of a function are the functions or methods of the function

The argument to a function or method is a function
map, filter and other methods take functions as parameters

//Pass the function as an argument
val c=(a : Int,b: Int,f:(Int,Int)=>Int) => {
  f(a,b)
}

//This allows the user to define the processing logic
println(c(3, 4, (x: Int, y: Int) => x  + y ))
println(c(3, 4, (x: Int, y: Int) => x  * y ))

The return value of the function is the function

def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = {
  val schema = if (ssl) "https://" else "http://"
  (endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query"
}

val domainName = "www.example.com"
//Get return function
def getURL = urlBuilder(ssl=true, domainName)
val endpoint = "users"
val query = "id=1"
val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String

2. Partial function

Partial function is a characteristic, which is specially used to deal with one type of data

//  Implementation of partial function in the form of filter
val list = List(1, 2, 3, 4, "hello")
val res: List[Int] = list.filter(x => x.isInstanceOf[Int]).map(x => x.asInstanceOf[Int] + 1)
res.foreach(println)

// Mode 2 matching mode
val res2: List[Any] = list.map(x => x match {
  //int type plus 1
  case x: Int => x + 1
  //Others are not handled
  case _ =>
})
res2.filter(x => x.isInstanceOf[Int]).foreach(println)

// Method 3 uses the data type input by partial function parameter 1 and the data type returned by parameter 2
val pp = new PartialFunction[Any,Int] {
  // Return true
  override def isDefinedAt(x: Any) = {
    x.isInstanceOf[Int]
  }
  // Execute next method
  override def apply(v1: Any) = {
    v1.asInstanceOf[Int]+1
  }
}

 //  list.map(pp).foreach(println)
 list.collect(pp).foreach(println)
  • PartialFunction is a trait (see the source code)
  • When constructing a partial function, the parameter form [Any, Int] is generic. The first represents the type of input parameter and the second represents the data type of returned data
  • When partial functions are used, all elements of the collection will be traversed. When the compiler executes the process, it first executes isDefinedAt(). If it is true, it will execute apply and build a new Int object to return
  • If isDefinedAt() is false, this element will be filtered out, that is, no new Int object will be built
  • The map function does not support partial functions, because the underlying mechanism of the map is all loop traversal, and the elements of the original collection cannot be filtered
  • The collect function supports partial functions

Partial function abbreviation

//The return type of the defined function is PartialFunction
def myPartialFunction: PartialFunction[Any, Int] = {
  case x: Int => x * x
}

//Use matching patterns directly in the collect() method
list.collect({case x:Int=>x*x}).foreach(println)

3. Matching pattern

Pattern matching is actually similar to the swich case syntax in Java, that is, conditional judgment is performed on a value, and then different processing is performed for different conditions.

However, Scala's pattern matching function is much more powerful than Java's swich case syntax, which can only match values. However, in addition to matching values, Scala's pattern matching can also match types, elements of Array and List, case class es, and even options with or without values.

For spark, the pattern matching function of Scala is also extremely important. The pattern matching function is widely used in the spark source code.

Basic syntax:

_  match {
	case _ 2 => TODO
	case _1 => TODO
	case _ => 
}

(1) Value matching

val str:String = "111"

str match {
  case "111" => println("111")
  case "222" => println("222")
}

(2) Data type matching

val  arr = Array(1,"a",1.40)
// Generate a random integer data and randomly fetch an element in the array
val index = Random.nextInt(arr.length)
arr(index) match {
  case x:Int => println(s"This is a Int Value of type $x")
  case x:String => println(s"This is a String Value of type $x")
  case x:Double => println(s"This is a Double Value of type $x")

  // You can match collection types and data types
  case x:Array[Int]=>println("This is an array")
  case x:List[String]=>println("List aggregate")
  case x:Map[String,Int]=>println("map aggregate")
  case x:MyUser=>println("MyUser")
  case _ =>println("I don't know what type")
}

(3) Internal data structure analysis

tuple

val tap: ((String), (Int), (String)) = Tuple3("3",1,"2")
tap match {
  //Number of matching tuple elements
  case Tuple3(_,_,_) => println(s"This is a tuple")
  //Match the number of tuple elements and get the first value
  case (x,_,_) => println(s"This is a tuple+$x")
  //Match the tuple with the first value of 3 and the number of elements of 3
  case ("3",x,_) => println(s"This is a tuple+$x")
  //Number of matching tuple elements and element type
  case (x:String,y:Int,z:String) => println(s"This is a tuple+$x")
}

Array

val arr1: Array[Any] = Array[Any]("abc",12,3,4,5,6)
arr1 match {
  //Match the number of array elements and get the first value
  case Array(x,_,_,_,_,_) => print(s"$x")
  //Number and type of matched array elements
  case Array(x:String,_,_,_,_,_) => print(s"$x")
  //Matches an array with 'abc' as the first element
  case Array("abc",_*) =>print("This array is expressed as a string abc start : "+arr.length)
  //Take this when none of the above meets the requirements
  case _ => println("hah")
}

List

x match {
  //Matches a List with only one element
  case head::Nil => println(s"With only one element Lis")
  case List(_) => println(s"With only one element Lis")
  case List(x) => println(s"With only one element Lis")
  
  //Number of matching List elements
  case List(_,_,x,_,_,_) =>println(s"With 5 elements list  The third position is $x")
  case head::second::thr::Nil =>println(s"3 Of elements list The first element of the collection is $head")

  //Match List element type
  case List(x:Int,_*) => println(s"with $x initial  List")
  
  //Match List specified elements
  case List(1,_*) => println(s"Beginning with 1 List")
  
  //In this way, you can match all list elements. Head is the first element, tail is the second element, and a is all the remaining elements
  case head::tail::a => println(s"$a")
}

Sample class

val user =User(1,"zss",13)

user match {
  case User(_,_,_) =>
  case User(1,_,_) =>
  case User(x:Int,y:String,z:Int) =>
}

Option

val map = Map("yangzi"->27,"reba"->30 ,"yangmi"->41)
val res: Int = map.get("fengjie") match {
  case Some(v) => v
  case None => -1
}

Map collections and custom classes do not support pattern matching structure resolution because structure resolution is essentially implemented by calling the unapply method of the original class. The sample class implements the unapply method by default and supports structure parsing.

4. Closure

A closure is a function whose return value depends on one or more variables declared outside the function.

Generally speaking, closures can be simply regarded as another function that can access local variables in a function.

var factor = 3  
val multiplier = (i:Int) => i * factor  

There are two variables in the multiplier: i and factor. One i is the formal parameter of the function. When the multiplier function is called, i is given a new value. However, factor is not a formal parameter, but a free variable, which is defined outside the function.

The function variable multiplier thus defined becomes a "closure" because it refers to the variables defined outside the function. The process of defining the function is to capture the free variable to form a closed function.

5. Detailed explanation of Coriolis

A function with multiple parameter lists is a coriolised function. The so-called parameter list is a list of function parameters enclosed in parentheses

The greatest significance of curry is to transform the function equivalence of multiple parameters into the cascade of multiple single parameter functions, so that all functions are unified and convenient for lambda calculus. In scala, curry is also helpful for type deduction. scala's type deduction is local. In the same parameter list, the subsequent parameters cannot be deduced with the help of the previous parameter types. After curry, they are placed in two parameter lists, and the parameters in the latter parameter list can be deduced with the help of the parameter types in the previous parameter list. This is why the definition of foldLeft is curry

Advantages of Coriolis function:

  • Facilitate type derivation and data evolution
//Pass in a list to find whether there are qualified values in the list
def findElement[T](ls: List[T], f: T => Boolean): Option[T] = {
  ls match {
    case Nil => None
    case head :: tail => if (f(head)) Some(head) else findElement(tail, f)
  }
}
//Corellized form
def findElement2[T](ls: List[T])(f: T => Boolean): Option[T] = {
  ls match {
    case Nil => None
    case head :: tail => if (f(head)) Some(head) else findElement2(tail)(f)
  }
}
//Common methods cannot calculate their own type from the contents of the previous List[Int]. The type must be specified after int E
val opt: Option[Int] = findElement(List[Int](1, 2, 3, 4, 5, 6), (e: Int) => e > 2)
//The Coriolis method will calculate that its type is int e according to the contents of the previous List[Int], and there is no need to specify the type
val opt2: Option[Int] = findElement2(List[Int](1, 2, 3, 4, 5, 6))((e) => e > 2)

opt2 match {
  case None => println("No value")
  case Some(value) => println(s"The element found is: $value")
}
  • Reduce parameter passing of methods
def  mkSal(deptno:Int , sal:Int)(comm:Int):Int={
  deptno+sal+comm
}

//When the parameters in the preceding parentheses remain unchanged, you can no longer pass the preceding parameters
val f: Int => Int = mkSal(1, 2)
println(f(3))
println(f(4))
  • Separate data from processing logic
def add(x:Int,y:Int)(f:(Int,Int)=>Int):Int={
  f(x,y)
}

val f: ((Int, Int) => Int) => Int = add(1, 2)
val i1: Int = f(_ + _)
val i2: Int = f(_ - _)

5. Implicit explanation

The content decorated with implicit is implicit content. The implicit feature is that it will be automatically applied when encountering an adaptive type

significance:

  • Make static types dynamic
  • Add functionality to an existing class library
  • Implicit proxies enhance a class or method

All implicit values and implicit methods must be placed in the object.

(1) Implicit variable

If two matching implicit variables are found in the scope, an error will be reported, so there can be no implicit variables of the same type in the scope

If a method has multiple parameters, to realize the implicit conversion of some parameters, you must use Coriolis. The implicit keyword appears later and can only appear once

//Implicit variables variables modified with implicit are implicit variables
implicit val str1 = "java Basic teacher"
implicit val age= 32

def show(name:String)(implicit msg:String,age:Int): Unit ={
  println(s"$name yes $msg,this year $age Years old")
}
//Implicit parameters can be passed automatically without passing during method call: the premise is that a corresponding implicit variable can be found from the context
show("zss")

(2) Implicit conversion

When the Scala compiler performs type matching, if no suitable candidate can be found, implicit conversion provides another way to tell the compiler how to convert the current type to the expected type.

The core is to define the implicit conversion function. According to the signature of the implicit conversion method, Scala will automatically transfer the object defined by the parameter type received by the implicit conversion method into the implicit conversion method, convert it into another type of object and return it.

When is implicit conversion required?

  • When an object calls a method or member that does not exist in the class, the compiler automatically implicitly converts the object
  • When the parameter type in the method is inconsistent with the target type

Limitations of implicit conversion:

  • implicit keyword can only be used to modify methods and variables (parameters)
  • The method of implicit conversion is valid only in the current scope. If the implicit method is not defined in the current scope (for example, it is defined in another class or contained in an object), it must be imported through the import statement

(3) Implicit function

An implicit function is only related to the parameter type and return type of the function, not the function name. Therefore, there cannot be implicit functions with different names of the same parameter type and return type in the scope.

// Implicit function when using File, it is found that there is an implicit conversion of File = > bufferedsource method in the context
// Enhance and extend the function items of the class
implicit  def richFile(file:File): BufferedSource ={
   val bs: BufferedSource = Source.fromFile(file)
   bs
 }
 
val file = new File("d://user.txt")
//The implicit conversion file can directly call the BufferedSource method getLines
val lines: Iterator[String] = file.getLines()

Method has an implicit function in its argument list

When calling a method, the compiler can automatically call the defined implicit function without passing a function, or pass in a user-defined function

//implicit function
implicit def add(x:String,y:Int): String ={
  x+y
}

//The parameters of normal functions use implicit parameters or implicit functions
def m1 ( x:Int, y:String)(implicit f:(String,Int)=>String):String={
    f(y,x)
}
println(m1(10, "nihao"))//Output: nihao10

(4) Implicit class

Similar to decorator mode, wrap a class to enhance its functionality

Implicit class constraints

  • An implicit class must have a primary constructor with one argument
  • Must be defined in another class/object/trait (cannot be defined independently)
  • An implicit class constructor can only take one parameter that is not an implicit modifier
  • The scope cannot have the same member variable, function and object name as the implicit class type
object ImplicitContext{
//An implicit class can only enhance one type of class, class B or subclass
  implicit class RichB(b:B){
    def multiply(x: Int, y: Int) = {
      x * y
    }
  }
}

class B {
  val add = (x: Int, y: Int) => {
    x + y
  }
}

def main(args: Array[String]): Unit = {
  val b = new B   // Class B and its subclasses
  //Call your own method
  println(b.add(12, 12))
  //Introducing implicit classes
  import ImplicitContext._
  //Calling methods of implicit classes
  println(b.multiply(12, 12))
}

(5) Using implicit transformation to sort custom classes

Defining an implicit comparator Ordering

implicit  val  ording: Ordering[User] = new Ordering[User] {
  override def compare(x: User, y: User): Int = {
    y.age - x.age
  }
}

val users  = List(new User(1, "zss", 13), new User(2, "lss", 23))
//When the sorted method is called, the comparator is implicitly passed in as a parameter
println(users.sorted)

Define an implicit method to convert User to Ordered
Note: this method cannot get the attribute value in the compare method

implicit def UserSored[User](user:User): Ordered[User] = {
  new Ordered[User] {
    override def compare(that: User): Int = {
      -(that.hashCode() - user.hashCode())
    }
  }
}

val users  = List(new User(1, "zss", 13), new User(2, "lss", 23))
//When the sorted method is called, the User is implicitly converted to Ordered 
println(users.sorted)

Defining implicit functions using generics

implicit  def t2Com[T](t:T):Ordered[T]={
      new Ordered[T] {
        override def compare(that: T): Int = that.hashCode() - t.hashCode()
      }
    }

//Use implicit functions in methods
def max[T](x:T,y:T)(implicit order:T=>Ordered[T]): T ={
      if(x > y) x else y
    }
//You can also not write implicit parameters, which will be automatically implicitly converted
def max[T](x:T,y:T) ={
      if(x > y) x else y
    }

6. Generic explanation

Scala's generics are similar to those in java. Generics can be defined on classes, methods and attributes to constrain types!
If we require that the parameters of the function can accept any type. You can also use generics, which can represent any data type.

(1) Generic class

//Define generics on classes  
class GenericClass[K,V](k:K,v:V) {
  def show(): Unit ={
    println(k+"----->"+v)
  }
}

object  Test1{
  def main(args: Array[String]): Unit = {
    // You do not need to specify a type for automatic inference 
    val gc = new GenericClass("Jack",999.99)
    // Specify type
    val gc1 = new GenericClass[String , String]("jim", "cat")
    // Specify the type. If the incoming data matches the type, an error will be reported
    val gc2 = new GenericClass[String , Int]("jim", "cat")
    gc.show()
  }
}

(2) Generic method

To use generics in a method, you must first define generics after the method name

// Find the maximum of two numbers int double float
// Define generic methods to receive arbitrary data types
def max[T](x:T,y:T): T ={
  if(x > y) x else y
}
// Use the implicit function to convert t into a name T type that can be compared. You can use > to compare
implicit  def t2Com[T](t:T):Ordered[T]={
 new Ordered[T] {
   override def compare(that: T): Int = t.hashCode() - that.hashCode()
 }
 }
// Receive a collection List of any type to get the data of all even index positions
def getElements[E](ls:List[E]): List[E] ={
  val es = for (elem <- 0 to ls.length - 1 if elem % 2 == 0) yield ls(elem)
  es.toList
}

val res1: List[Int] = getElements[Int](List[Int](1, 3, 5, 7, 9, 2, 4, 6, 8))
val res2: List[String] = getElements[String](List[String]("tom","jim","cat","rose"))

(3) Generic upper and lower bounds

Upper and lower bounds in java:

  • < T extends a > type A is the upper bound
  • < T super a > type A is the lower bound

Upper and lower bounds in Scala

  • Upper bound [T <: a] type A is the upper bound, and the incoming type must be type A and its subclasses
  • Lower bound [t >: a] type A is the lower bound, and the passed in type must be type A and its parent class

If the upper bound of the generic is defined as the Ordered class, the generic objects can be compared

// B is the upper bound. The upper bound parameter must be the specified type and its subtypes
def test1[T <:B](t:T): Unit ={

}
// B is the lower bound, and the parameter is the specified type and its parent type
def test2[T >:B](t:T){
}

class A extends B
class B {
}
class C  {
}

test1(new A)
test1(new B)
test1(new C)  //Error C is not a subtype of B

//After specifying the lower bound, the following writing method will not report an error, because [] does not write. The default is Any, which is the parent of all classes 
test2(new A)
//Writing like this will report an error
test2[A](new A)

(4) Context definition

When the implicit parameters are explained, an implicit parameter is needed to implicitly convert the type in the generic type to the corresponding type

def myMax(x1:T,x2:T)(implicit o:T=>Ordered[T])={
   if(x1 > x2) x1 else x2
 }

7,IO

(1) Input

Get input file source: Source.fromFile("")

(1) Input line

object LineIO {
  def main(args: Array[String]): Unit = {
   // read file
    val bs: BufferedSource = Source.fromFile("d://data.txt")
    // Get all rows
    val lines: Iterator[String] = bs.getLines()
    // Traverse all rows
    for (line <- lines) {
      println(line)
    }
    // Row list
    val list: List[String] = lines.toList
    //Row array
    val array: Array[String] = lines.toArray
    // Entire string
    val content: String = lines.mkString
    // Release resources
    bs.close()
  }
}

(2) Input byte

object ByteIo {
  def main(args: Array[String]): Unit = {
    val bs: BufferedSource = Source.fromFile("d://data.txt")
    // Get input stream object
    val reader: InputStreamReader = bs.reader()
    //Skip the specified length to the specified position
    reader.skip(1)
    // Read a byte 
    val byte: Int = reader.read()
    println(byte) // 99
    reader.close()
    bs.close()
  }
}
def main(args:Array[String]):Unit={
  val file = new File("F:\\info.bin")
  val in = new FileInputStream(file)
  val bytes = new Array[Byte](file.length.toInt)
  in.read(bytes)
  in.close
}

(3) Read other data sources

//Read from URL
val source= Source.fromURL("http://www.baidu.com","UTF-8")
val lineIterator =source.getLines
for(l<-lineIterator){
  println(l.toString())
}
//Read from the given string -- useful for debugging
val source2= Source.fromString("Hello DOIT")
println(source2.mkString)//Hello DOIT
//Read from standard input
val in: BufferedReader = Console.in
println(in.readLine())

(2) Output

//Append when True
val out = new FileWriter("D:\\index.txt",true)
for (i <- 0 to 15){
out.write(i.toString)
out.append"111")
out.close()
val writer = new PrintWriter("D:\\index.txt")
for(i <- 1 to 10)
writer.println(i)// This is auto wrap. Don't add any more \ remember

writer.write("111")
writer.append("111")
writer.close()

Topics: Scala Big Data