Scala--尾递归--泛型--类型上下限界定--多重界定--视图界定--型变(协变--逆变)--隐式转换--SAM
0.尾递归
可以进行优化,将递归转换成循环实现,避免栈的溢出。
def sum(args:Seq[Int]):BigInt = {
if(args.isEmpty) 0 else args.head + sum(args.tail)
}
sum(1 to 10000) //溢出
//实现了尾递归
//实现尾递归sum2函数接受两个函数x:Seq[Int]:序列 part:BigInt:累加求和每次产生的部分值
def sum2(x:Seq[Int] , part :BigInt):BigInt = {
if(x.isEmpty) part else sum2( x.tail ,x.head + part)
}
sum2(1 to 100000 , 0) //ok
def out(str:String):Unit= {
out(str)
println(str)
}
/**
在Scala中它可以将尾递归转换成循环。就不会出现栈溢出了。
* 尾递归:它把这个递归函数,若最后一条语句是递归本身。它可以把这种递归转成循环。就可以避免栈溢出。
StackOverflowError(栈溢出异常)
*/
object TailRecursiveDemo{
//用递归计算累加和,递归函数必须要定义返回值类型
def sum(list:List[Int]):Int={
if(list.isEmpty)0 else list.head +sum(list.tail)
//这里加了判断,它最后的语句就不是递归是0,所以不是尾递归
}
//这个函数不是尾递归,为什么?因为它最后一条语句不是递归
def main(args: Array[String]):Unit={
val list=List(1 to 100:_*)
println(sum(list))
}
}
1.泛型
//定义泛型类
class Pair[T,S](val first:T,val second:S)
泛型的使用
object GenericTypeDemo{
def main(args:Array[String]):Unit={
//Pair[T,S]这个决定后面的属性:(var first:T,var second:S)是什么类型
class Pair[T,S](var first:T,var second:S)
//
val p1=new Pair[Int,String](1,"tomas")
//类型推断
val p2=new Pair(1,"tomas")
--------------------------------
//方法定义泛型
def middle[T](arr:Array[T]):T={
arr(arr.length/2)
}
//调用函数
var arr=Array(1,2,3,4)
println(middle(arr)) //3
//asInstanceOf这个也是泛型
val obj=""
obj.asInstanceOf[String]
}
}
[Java里面的泛型]
public static <T>T getMiddle(List<T>list){
return list.get(list.size()/2);
}
//调用java里面的泛型
【Maven】
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
@Test
public void testMethod(){
List<Integer>list=new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(getMiddle(list)); //2
}
//在方法调用中指定类型,通过对象来调用
public static <T>T getMiddle(List<T>list){
return list.get(list.size()/2);
}
@Test
public void testMethod(){
List<Integer>list=new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
//在方法调用中指定
System.out.println(getMiddle(list));
}
2.类型变量的界定
类型变量界定:指的就是范围(上限和下限)
现在你想要添加一个方法,产生较小的那个值:产生较小值的话。它要能够比较大小
class Pair[T](val first:T,va; second:T){
//在上面直接定义了一个T作为一个泛型,在方法体中first.compareTo(second)<0)小于他,小于0就返回。就是T或者first是否有没有这个方法?它不一定有。所以它可能是错的。为了保证它一定有可以添加添加一个上界T<:Comparable[T]> 上限,就是要小于T<:Comparable[T]>
def smaller=if(first.compareTo(second)<0)first else second //错误
}
这是错的----我们并不知道first是否有compareTo方法。要解决这个问题,我们可以添加一个上界T<:Comparable[T]> 上限
//定义上界限
//class Pair[T ](var first:T,var second:T){//它没有<:Comparable[T]识别不了
class Pair[T <:Comparable[T]](var first:T,var second:T){
//定义一个方法要取,取它两个大的值。T
//获取两个属性中较大的一个
def max():T={
if(first.compareTo(second)>0)first else second;
}
}
val p1=new Pair(100,200)//报错,它不能比较大小在scala中它并没有Comparable
val p1=new Pair("100","200")//200
//注意了在Java里面和scala里面字符窗String 有什么区别
type String =java.lang,String //scala里面的串就是java里面的串,它就是一个别名
type Class[T]=java.lang.Class[T]
println(p1.max())
Int的是AnyVal,Int的不是别名。它没有Comparable的特点。所以它比较不了。
如果要向实现数字的大小比较,需要用到视图界定
[Int的源码翻看]
final abstract class Int private etends AnyVal{}
//考察下界
class Animal
class Dog extends Animal
class Jing8 extends Dog
//T,没有任何说法,什么都可以
//Pari是有两个属性的,都是T类型.现在添加一个方法将first替换掉,保留第二个不变第一个变掉
class Pair[T](var first :T,var second:T){
//R需要是T的超类
//def replaceFirst[R >:T](newele:R)={
def replaceFirst[R >:T](newfirst:R)={
//new Pair(newele,second)
new Pair(newfirst,second)
}
}
val d1=new Dog()
val d2=new Dog()
val p1=new Pair[Dog](d1,d2)
val jing8=new Jing8()
val p2= p1.replaceFirst(jing8)
//加了泛型它就有要求,需要给方法显示指定泛型
val p2= p1.replaceFirst[Jing8](jing8)
//给它一个新的类型
val a1=new Animal()
p1.replaceFirst[Animal](a1)
println(p2)
1.类上泛型
class Pair[T,S](var first:T , var second:S)
val p = new Pair[Int,String](100,"tom")
val p = new Pair(100,"tom")
2.方法上泛型
def mid[T](arr:Array[T]) = arr(arr.length / 2)
mid[Int](Array(2,3,4))
mid(Array(2,3,4))
3.类型上限界定
class Pair[T <: Comparable[T]] (var a:T ,var b:T)
val p = new Pair("100","100")
4.类型下限界定
class Pair[T >: Jing8](var fst:T ,var sec:T){
// 替换一个新的元素 ele:R代表ele的元素类型是R类型。
def replaceFirst[R >: T](ele:R) {
//new Pair(ele , sec) ;表示下界,R必须是父类才可以
new Pair(ele , sec) ;//把原sec放这替换ele
}
}
val d1 = new Dog()
val d2 = new Dog()
val p1 = new Pair(d1,d2)//
val jing8 = new Jing8()
//错误,jing8是dog的子类,不符合下界范围
p1.replaceFirst[Jing8](jing8)//替换,必须要把泛型写上[Jing8]
val a1 = new Animal()
//OK , 符合下界范围
p1.replaceFirst[Animal](a1)
3.视图界定
视图界定
<% //相当于模糊查询,能够隐式转换成目标类
class Pair[T<% Comparable[T]](var first:T,var second:T){
//获取两个属性中较大的一个
def max():T={
if(first.compareTo(second)>0)first else second;
}
}
<% //相当于模糊查询,能够隐式转换成目标类也可以。
class Pair[T <% Comparable[T]] (var a:T , var b:T)
val p = new Pair(100,200)
上下文界定
视图界定T<%V要求必须存在一个从T到V的隐式转换。上下文界定的形式为T:M,其中M是另一个泛型类。它要求必须存在一个类型为M[T]的"隐式值"。
class Pair[T:Ordering] //Ordering必须存在隐式值才行
4.多重界定
先大于后小于
//R>:Jing8 <:Animal,
类型变量可以同时有上界和下界。写法为:
T>:Lower <:Upper
class Animal
class Dog extends Animal
class Cat extends Animal
class JiaFeiCat extends Cat
class Jing8 extends Dog
class LocalJing8 extends Jing8
class Pair[T](var first:T,var second:T){
//现在的要求就是替换的数它得是Animal的子类同时也是LocalJing8的父类
def replaceFirst[R <:LocalJing8<:Animal ](ele:R)={
new Pair(ele,second)
}
}
val a1=new Animal
val d1=new Dog
val d2=new Dog
val jing81=new Jing8()
val jing82=new Jing8()
val local1=new LocalJing8()
val local2=new LocalJing8()
val c1=new Cat()
val p1=new Pair[Dog](d1,d2)
//对它进行替换,它必须是Animal的子类LocalJing8的父类
val pp=p1.replaceFirst[Animal](a1) //动物本身可以
val pp=p1.replaceFirst[Cat](C1)//do not conform to method replaceFirst's type parameter bounds 它不符合参数类型的边界
val pp=p1.replaceFirst[LocalJing8](local1)//可以进来了,它是包含的
println(pp) //200
//就定义一个Jing8是不可以的def replaceFirst[R <:Jing8<:Animal ](ele:R)={
def replaceFirst[R <:Jing8<:Animal ](ele:R)={
new Pair(ele,second)
}
val p1=new Pair[Dog](d1,d2)
val pp=p1.replaceFirst[LocalJing8](local1)
println(pp)//do not conform to method replaceFirst's type parameter bounds 它不符合参数类型的边界
//R >: Jing8 <: Animal , 先大于后小于。
class Pair[T] (var first:T , var second:T) {
//能传给方法给replaceFirst的是谁?
def replaceFirst[R >: LocalJing8 <: Animal] (ele :R) = {
new Pair(ele, second) ;
}
}
5.型变
class Person
class Student extends Person
class Teacher extends Person
class Pair[T](var first:T,val second:T){
def makeFriends(frd:Pair[Person])={
println("hello world")
}
}
val p1=new Pair[String]("bob","alice")
val p2=new Pair[Person](new Person,new Person)
如果Student是Person的子类,那么我可以用Pair[Student]作为参数调用makeFriends吗?
缺省情况下,这是个错误。尽管Student是Person的子类型,但Pair[Student]和Pair[Person]之间没有任何关系。现在是让Person和Student之间有继承关系。
p1.makeFriends(p2)
1)协变,就是变化方向相同[+T]就是协变
按照相同方向改变。
class Person
class Student extends Person
class Teacher extends Person
//协变,变化方向相同
class Pair[+T](val first: T, val second: T) {
//Person和Student是有继承关系的和Pair和Person,Student没有继承关系,如果要想有这样的关系就编写在定义Pair类时表明这一点
class Pair[+T](val first:T,val second:T)
def makeFriends(frd : Pair[Person]) = {
println("hello world")
}
}
val p1 = new Pair[Person](new Person , new Person)
val p2 = new Pair[Student](new Student , new Student)
p1.makeFriends(p2)
2)逆变 Student是Person的子类Pair Person类的父类
成相反方向变换.
Pair[Student]是Pair[Person]的父类。
6.隐式转换
隐式转换函数(implicit convcrsion function)指的是那种以implicit关键字声明的带有单个的函数。这样的函数将被自动应用,将指从一种类型转换为另一种类型
/**
隐式转换测试
*/
object ConvertDemo{
def main(args:Array[String]):Uint={
//定义分数类
// case class Fraction(val top:Int,val bot:Int){
// def mulby(f2:Fraction)={
// Fraction(top*f2.top,bot*f2.bot)
// }
}
//定义隐式函数
implicit def int2Frction(n:Int)={
Fraction(n,1)
}
val f1=Fraction(1,2)
val f2=Fraction(2,3)
val f3=Fraction(f2)
println(f3) //Fraction(2,6)
println(f3.mulby(4)) //Fraction(8,6)
//如果你函数的参数只有一个,那你就可以把这个方法变成操作符放中间。
println(f3 mulby 4)//典型的中置 //Fraction(8,6)
}
}
工作原理:都是引进了一些隐式转换函数,当你把不符合条件的参数变成符合条件的对象
[工具类]
//object 是单例对象 里面的方法都是静态的
object MyConverterUtil{
implicit def int2Fraction(){}
}
[JavaConversions(scala.collection)源码解析]
Object JavaConversions extends WrapAsScala with WrapAsJava
里面定义了很多隐式转换
implicit def as ScalaIterator[A](it:ju.Iterator[A]):Iterator[A]=it match{
case IteratorWrapper(wrapped)=>wrapped
case _=>JIteratorWrapper(it)
}
..
//常用的 它的参数是List ju=java.util
//可以这样引用命名空间 import java.{lang =>jl,util=>ju},java.util.{concurrent=>jc}
implicit def asScalaBuffer[A](l:ju.List[A]):mutable.Buffer[A]=l match{
case MutableBufferWrapper(wrapped)=>wrapped
case _=>new JListWrapper(l)
}
[RichInt(RichInt(scala.runtime))]//这个类是可以比较大小的。
//RichInt实现了ScalaNumberProxy,它实现了scala数字的代理。它就可以比较大小
final class RichInt(val self:Int)extends AnyVal with ScalaNumberProxy[Int]with RangedProxy[Int]{}
//Comparable有序的
trait Ordered[A]extends Any with java.lang.Comparable[A]{}
//Ordered的类型就是
type Ordered[T]=scala.math.Ordered[T]
val Numeric=scala.math.Ordered
//OrderedProxy这个是代理,Ordered它有接口的实现
trait OrderedProxy[T]extends Any with Ordered[T]with Typed[T]{
protected def ord:Ordering[T]
def compare(x:T)=ord.compare(self,y)
}
Int.scala
implicit scala.language.implicitConversions //implicitConversions隐式转换
引入隐式转换导包:
import com.horstmann.impatient.FractionConversions._
在导的时候可以带下划线,也可以导具体的方法。
import MyCoverterUtil //如果不想全部导进来就可以导局部的
import MyCoverterUtil.int2Fraction //导入具体的隐式转换函数。
//隐式值测试
object ImplictValueDemo{
def main(args:Array[String])={
//修饰函数
def decorate(pref:String="<<<",str:String,suf:String=">>>")={
pref+str=suf
}
//
//带名参数
println(decorate(str="hello"))
//假如你有很多方法都有implicit这个类型的参数,在绝大多数情况下都以这个值做为参数传递。就可以把这个值定义为默认值。隐式参数是不 可以完成作业的,它一定要跟隐式值配合。
def decorate(implicit pref:String,str:String)={
pref+str
}
//抛异常:
//如果在引入隐式转换的时候,有多个的时候,会报异常。此时可以引具体的方法
import MyConverterUtil.prefix
println(decorate(str="hello"))
//implicit val+字符串pref:String就成了隐式值
implicit val pref:String="<<<"
println(decorate())
}
}
典型的
val list:List[Int]=List(1 to 10:_*)
//implicit bf隐式参数就可以找到一个隐式值将它传进来。这也是柯里化(一次只处理一个参数)的好处。隐式参数定在一起没法弄。
//map[B,That](f:(String)=>B)(implicit bf:CanBuildFrom[List[String],B,That])That
list.map(_*2)
1.隐式转换函数
object Utils{
implicit def int2Fraction(n:Int) = {
Fraction( n, 1)
}
}
import Utils._
3 * Fraction(1,2)
2.隐式参数
//柯里化函数
implicit val pref:String = "<<<"
def dec(str:String)(implicit pre:String)
dec("hello")
3.隐式值
implicit val xx:String = "<<<"
def dec("hello")
7.SAM
SAM(函数在java里面就是接口)(隐式转换函数的应用)
single abstract method(单个抽象方法)
object SAMDemo{
//每次实现奇数加1
var click:Int=0
def main(args:Array[String]):Unit={
val frame=new JFrame()
frame.setTitle("hello swing")
frame.setBounds(50,50,200,100)
frame.setVisible(true)
//加按钮之前设置一下布局为空
frame.setLayout(null);
val btn=new JButton()
btn.setBounds(0,0,100,50)
btn.setText("ok")
//实现点击一下加1
btn.addActionListener(new ActionListener{
//ActionListener public interface ActionListener extends EventListener 它就一个 public void actionPerformed(ActionEvent e)抽象方法。。典型的匿名类对象
override def actionPerformed(e:ActionEvent)={//常规的Java实现
//给按钮一个点击事件,每次点击一下加1 ,scala可以做sam抽象,一个抽象方法
click+=1
println(click) //1,2,3,,4,5,6,,7,....15
}
})
//(e:ActionEvent)=>{click_+=1;println(click)这个传的是一个函数,而不是需要的事件监听器。为了让这种方式成为可能,就要做一个隐式转换。隐式转换就是直接能把(e:ActionEvent)=>{click_+=1;println(click)转换成ActionListener实现就可可以了
// btn.addActionListener((e:ActionEvent)=>{click_+=1;println(click)})
//从函数到func2ActionLisnter的转换func2ActionLisnter()的参数是一个函数,{click_+=1;println(click)函数本身没有返回值。参数是ActionEvent类型
//定义隐式转换函数,将函数转换成actionListen对象(SAM转换)
implicit def func2ActionListnter(f:(ActionEvent)=>Unit)={
new ActionListener{
override def actionPerformed(e:ActionEevent)={
f(e)
}
}
}
btn.addActionListener((e:ActionEvent)=>{click+=1;println(click)});
frame.add(btn)
//
frame.setVisible(true)
}
}