Scala series (13) - implicit conversion and implicit parameters

First, the implicit conversion

1.1 implicit conversion

Implicit conversion means is implicita conversion function with a single key parameter declaration, it values are converted from one type to another type, prior to use no type of function. Examples are as follows:

// 普通人
class Person(val name: String)

// 雷神
class Thor(val name: String) {
  // 正常情况下只有雷神才能举起雷神之锤
  def hammer(): Unit = {
    println(name + "举起雷神之锤")
  }
}

object Thor extends App {
  // 定义隐式转换方法 将普通人转换为雷神 通常建议方法名使用source2Target,即:被转换对象To转换对象
  implicit def person2Thor(p: Person): Thor = new Thor(p.name)
  // 这样普通人也能举起雷神之锤
  new Person("普通人").hammer()
}

输出: 普通人举起雷神之锤

1.2 Implicit conversion rules

Not what you use implicitAfter the conversion, implicit conversion will certainly occur, such as the above if you do not call the hammer()method when ordinary people or ordinary people would. Usually the program tries to perform an implicit conversion in the following cases:

  • When the object is a member of a non-existent access method that is called does not exist or access to member variables does not exist;
  • When the object call a method, this method has, but declare the parameter passed in the method parameter mismatch.

In the following three cases the compiler does not attempt to perform an implicit conversion:

  • If the code to compile implicit conversion is not used without the use of an implicit conversion;
  • The compiler does not attempt to simultaneously perform multiple conversion, for example convert1(convert2(a))*b;
  • There is ambiguity conversion, the conversion will not occur.

Here first explain the ambiguity, the above code modified as follows, since the two implicit conversions are in effect, so there is an ambiguity:

//两个隐式转换都是有效的
implicit def person2Thor(p: Person): Thor = new Thor(p.name)
implicit def person2Thor2(p: Person): Thor = new Thor(p.name)
// 此时下面这段语句无法通过编译
new Person("普通人").hammer()

Secondly, and then explain a number of issues of conversion:

class ClassA {
  override def toString = "This is Class A"
}

class ClassB {
  override def toString = "This is Class B"
  def printB(b: ClassB): Unit = println(b)
}

class ClassC

class ClassD

object ImplicitTest extends App {
  implicit def A2B(a: ClassA): ClassB = {
    println("A2B")
    new ClassB
  }

  implicit def C2B(c: ClassC): ClassB = {
    println("C2B")
    new ClassB
  }

  implicit def D2C(d: ClassD): ClassC = {
    println("D2C")
    new ClassC
  }

  // 这行代码无法通过编译,因为要调用到printB方法,需要执行两次转换C2B(D2C(ClassD))
  new ClassD().printB(new ClassA)
    
  /*
   *  下面的这一行代码虽然也进行了两次隐式转换,但是两次的转换对象并不是一个对象,所以它是生效的:
   *  转换流程如下:
   *  1. ClassC中并没有printB方法,因此隐式转换为ClassB,然后调用printB方法;
   *  2. 但是printB参数类型为ClassB,然而传入的参数类型是ClassA,所以需要将参数ClassA转换为ClassB,这是第二次;
   *  即: C2B(ClassC) -> ClassB.printB(ClassA) -> ClassB.printB(A2B(ClassA)) -> ClassB.printB(ClassB)
   *  转换过程1的对象是ClassC,而转换过程2的转换对象是ClassA,所以虽然是一行代码两次转换,但是仍然是有效转换
   */
  new ClassC().printB(new ClassA)
}

// 输出:
C2B
A2B
This is Class B

1.3 introduces implicit conversion

Implicit conversion can be defined in the following three areas:

  • In the original definition of the type of associated objects;
  • Direct scope defined in the context of the execution code;
  • Uniform definition in a file, import use time.

We use the above method is equivalent to executing code directly defined scope, the following sample code is given the other two are defined:

In the original definition of an object type associated :

class Person(val name: String)
// 在伴生对象中定义隐式转换函数
object Person{
  implicit def person2Thor(p: Person): Thor = new Thor(p.name)
}
class Thor(val name: String) {
  def hammer(): Unit = {
    println(name + "举起雷神之锤")
  }
}
// 使用示例
object ScalaApp extends App {
  new Person("普通人").hammer()
}

In the definition of a common object :

object Convert {
  implicit def person2Thor(p: Person): Thor = new Thor(p.name)
}
// 导入Convert下所有的隐式转换函数
import com.heibaiying.Convert._

object ScalaApp extends App {
  new Person("普通人").hammer()
}

Note: Most definitions Scala's own implicit conversion function Predef.scala, you can open the source file viewer, can also be used in Scala interactive command line :implicit -vto see all the implicit conversion functions.


Second, a hidden parameter

2.1 implicit parameter

Defining a function or method may be used to mark implicitparameters, in this case, the compiler will find the default value, it is supplied to the function call.

// 定义分隔符类
class Delimiters(val left: String, val right: String)

object ScalaApp extends App {
  
    // 进行格式化输出
  def formatted(context: String)(implicit deli: Delimiters): Unit = {
    println(deli.left + context + deli.right)
  }
    
  // 定义一个隐式默认值 使用左右中括号作为分隔符
  implicit val bracket = new Delimiters("(", ")")
  formatted("this is context") // 输出: (this is context)
}

About the implicit parameter, there are two points to note:

1. We defined above formattedwhen using curried functions, if you do not use curried expression, according to the usual habit of writing only the following two:

// 这种写法没有语法错误,但是无法通过编译
def formatted(implicit context: String, deli: Delimiters): Unit = {
  println(deli.left + context + deli.right)
} 
// 不存在这种写法,IDEA直接会直接提示语法错误
def formatted( context: String,  implicit deli: Delimiters): Unit = {
  println(deli.left + context + deli.right)
} 

The first translation of the above wording will appear as shown in the following errorinformation, which you can also see implicitthe role of each parameter in the parameter list, which is obviously not the effect we want to reach, so the above wording adopted currying .

not enough arguments for method formatted: 
(implicit context: String, implicit deli: com.heibaiying.Delimiters)

2. The second question and implicit function as an implicit default values ​​can not exist ambiguity, or can not compile the following, example:

implicit val bracket = new Delimiters("(", ")")
implicit val brace = new Delimiters("{", "}")
formatted("this is context")

The above code will not compile, an error message appears ambiguous implicit values, there is a conflict that is implicit values.

2.2 introducing a hidden parameter

Introducing a hidden parameter introduced into and implicit conversion function is the same method, the following three ways:

  • The object definition implicitly associated parameters corresponding to the class;
  • Direct scope defined in the context of the execution code;
  • Uniform definition in a file, import use time.

We defined directly above sample program corresponds to the code execution context scope, examples of the other two methods are given below:

The object definition implicitly associated parameters corresponding to the class ;

class Delimiters(val left: String, val right: String)

object Delimiters {
  implicit val bracket = new Delimiters("(", ")")
}
// 此时执行代码的上下文中不用定义
object ScalaApp extends App {

  def formatted(context: String)(implicit deli: Delimiters): Unit = {
    println(deli.left + context + deli.right)
  }
  formatted("this is context") 
}

Uniform definition in a file, use the time to import :

object Convert {
  implicit val bracket = new Delimiters("(", ")")
}
// 在使用的时候导入
import com.heibaiying.Convert.bracket

object ScalaApp extends App {
  def formatted(context: String)(implicit deli: Delimiters): Unit = {
    println(deli.left + context + deli.right)
  }
  formatted("this is context") // 输出: (this is context)
}

2.3 using implicit implicit conversion parameter

def smaller[T] (a: T, b: T) = if (a < b) a else b

In Scala If a generic method for comparison of the size of the one shown above definition, you will find not compile. To carry out a comparison between the size of the object, Scala and Java, are required to achieve the object needs to be compared java.lang.Comparable interface. In Scala, the direct successor to Java in the Comparable interface is character Ordered, it inherited the compareTo method on the basis of additional defines the relationship between the characters method, source code is as follows:

trait Ordered[A] extends Any with java.lang.Comparable[A] {
  def compare(that: A): Int
  def <  (that: A): Boolean = (this compare that) <  0
  def >  (that: A): Boolean = (this compare that) >  0
  def <= (that: A): Boolean = (this compare that) <= 0
  def >= (that: A): Boolean = (this compare that) >= 0
  def compareTo(that: A): Int = compare(that)
}

So, we need to solve this problem in a generic, there are two methods:

1. View Definition

object Pair extends App {

 // 视图界定
  def smaller[T<% Ordered[T]](a: T, b: T) = if (a < b) a else b
 
  println(smaller(1,2)) //输出 1
}

T may view the limits defined by implicit conversion Ordered[T], i.e. the size of the object will be able to be compared. In the above code smaller(1,2)parameters 1and 2actually defined by Predefan implicit conversion method intWrapperis converted into RichInt.

// Predef.scala
@inline implicit def intWrapper(x: Int)   = new runtime.RichInt(x)

Why bother to do an implicit conversion, because Scala in Int type can not be compared directly because it does not implement Orderedcharacter, the real Orderedcharacter is RichInt.

2. using implicit implicit conversion parameter

After Scala2.11 +, the view definition is identified as abandoned, the official recommended to use the type defined solve the above problems, is essentially a hidden parameter using an implicit conversion.

object Pair extends App {

   // order既是一个隐式参数也是一个隐式转换,即如果a不存在 < 方法,则转换为order(a)<b
  def smaller[T](a: T, b: T)(implicit order: T => Ordered[T]) = if (a < b) a else b

  println(smaller(1,2)) //输出 1
}

Reference material

  1. Martin Odersky. Scala program (version 3) [M]. Press the electronic industry. 2018-1-1
  2. Kay .S. Horstman Fast learning Scala (2nd Edition) [M]. Press the electronic industry. 2017-7

More big data series can be found in personal GitHub open source project: programmers Big Data Getting Started

Guess you like

Origin www.cnblogs.com/heibaiying/p/11565848.html