scala

首页scala
29
Nov
0

隐式转换

作用:

把原本对象没有方法,通过隐式转换变为有

方法:
1.将方法或变量标记为implicit
2.将方法的参数列表标记为implicit
3.将类标记为implicit

使用方式:
1.隐式值:用于给方法提供参数
2.隐式视图:用于类型间转换或使针对某类型的方法能调用成功

例1:
def person(implicit name : String) = name //name为隐式参数 person: (implicit name: String)String
// 执行 直接会报错 因为没有传参
person()
// 但是加入一个隐式变量 再调用 就正常了
implicit val p = "mobin"
person()
// 不过再多定义一个隐式变量 就又不正常了 产生歧义 编译器不知道该找哪个了
implicit val p1 = "mobin1"
person() // error

例2:
def foo(msg : String) = println(msg)
foo(10) // 出错 因为参数要求字符串 传了一个INT进去

// 定义一个隐式转换视图(实际上就是调用int对象默认不存在的转换函数方法) 再去调用 就正常了
implicit def intToString(x : Int) = x.toString
foo(10)

例3:
class SwingType{
def wantLearned(sw : String) = println("兔子已经学会了"+sw)
}
object swimming{
implicit def learningType(s : AminalType) = new SwingType
}
class AminalType
object AminalType extends App{
// 导入类的所有方法
import com.mobin.scala.Scalaimplicit.swimming._
val rabbit = new AminalType
// 本来rabbit不存在wantLearned方法 但是隐式导入了def learningType(s : AminalType)方法, 具备了这个方法
rabbit.wantLearned("breaststroke")
}

例4:
class SwingType{
def wantLearned(sw : String) = println("兔子已经学会了"+sw)
}
package swimmingPage{
object swimming{
implicit def learningType(s : AminalType) = new SwingType //将转换函数定义在包中
}
}
class AminalType
object AminalType extends App{
//和例三的区别是 这里是导入包里的
import com.mobin.scala.Scalaimplicit.swimmingPage.swimming._
val rabbit = new AminalType

rabbit.wantLearned("breaststroke")         //蛙泳

}

原型为:
implicit def originalToTarget (<argument> : OriginalType) : TargetType

隐式类:
scala2.10以后提供
要求:
1.其所带的构造参数有且只能有一个
2.隐式类必须被定义在类,伴生对象和包对象里
3.隐式类不能是case class(case class在定义会自动生成伴生对象与2矛盾)
4.作用域内不能有与之相同名称的标示符

例5:
object Stringutils {
implicit class StringImprovement(val s : String){ //隐式类

  def increment = s.map(x => (x +1).toChar)

}
}
object Main extends App{
import com.mobin.scala.implicitPackage.Stringutils._
println("mobin".increment)
}

隐式转换的时机:
1.当方法中的参数的类型与目标类型不一致时
2.当对象调用类中不存在的方法或成员时,编译器会自动将对象进行隐式转换

隐式解析机制 即编译器是如何查找到缺失信息的,解析具有以下两种规则:
1.首先会在当前代码作用域下查找隐式实体(隐式方法 隐式类 隐式对象)
2.如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找
类型的作用域是指与该类型相关联的全部伴生模块,一个隐式实体的类型T它的查找范围如下:

(1)如果T被定义为T with A with B with C,那么A,B,C都是T的部分,在T的隐式解析过程中,它们的伴生对象都会被搜索
(2)如果T是参数化类型,那么类型参数和与类型参数相关联的部分都算作T的部分,比如List[String]的隐式搜索会搜索List的

伴生对象和String的伴生对象

(3) 如果T是一个单例类型p.T,即T是属于某个p对象内,那么这个p对象也会被搜索
(4) 如果T是个类型注入S#T,那么S和T都会被搜索

隐式转换的前提:
1.不存在二义性(如例1)
2.隐式操作不能嵌套使用(如 convert1(covert2(x)))+y
3.代码能够在不使用隐式转换的前提下能编译通过,就不会进行隐式黑铁

28
Nov
0

上界,下界,视图绑定和上下文边界

class Earth {

  def sound(){
    println("hello !")
  }
}
class Animal extends Earth{
  override def sound() ={
    println("animal sound")
  }
}
class Bird extends Animal{
  override def sound()={
    println("bird sounds")
  }
}
// 上界
println("<:上界测试")
def biophony[T <: Animal](things: Seq[T]) = things map(_.sound)
biophony(List(new Bird(),new Animal()))
biophony(Seq(new Animal, new Animal))

// 下界
println(">:下界测试")
//因为比这个类大的还有类Object 所以不确定是否有sound函数 所以编译通不过 .map(_.sound)
def biophony1[T >: Animal](things: Seq[T]) = things
//为什么传Bird()也不报错呢 因为返回的是Animal实例 自动as Animal了
//如果参数再传一个 new Moon()  那么返回的是 Object实例 因为不是我们定义的Animal
biophony1(List(new Earth(),new Animal(),new Bird())).map(_.sound())

println("<%视图绑定测试 ")
// 符号意思 T必须可以转化为Animal
def biophony2[T <% Animal](things: T) = {
  things.sound()
}
// 因为Earth是Animal的父类 所以我们传Earth是不可能转化为Animal的
// 如果不加隐式转换 则无法通过编译
implicit def autoChange(things:Earth) = new Animal
biophony2(new Earth)
// Bird本身是Animal的子类 所以直接可以通过
biophony2(new Bird)

// 上下文边界
println(":上下文边界测试 ")
// 意思是必须存在一个“T[Seq]”的隐式值
def biophony3[T:Seq](things: T) = {
  println(things)
}
// 这个隐式转换没有看懂
implicit val b = Seq(new Animal)
biophony3(new Animal)
28
Nov
0

逆变 协变 不变

class Animal {}
class Bird extends Animal {}
class Chicken extends Bird {}

//逆变
class Covariant1[-T](t:T){}
val cov01 = new Covariant1[Animal](new Animal)
val cov02:Covariant1[Chicken] = cov01

//协变
class Covariant2[+T](t:T){}
val cov11 = new Covariant2[Bird](new Bird)
val cov12:Covariant2[Animal] = cov11

//不变
class Covariant3[T](t:T){}
val cov21 = new Covariant3[Bird](new Bird)
val cov22:Covariant3[Bird] = cov21

例1:

class Earth {
  def sound(){
    println("hello !")
  }
}
class Animal extends Earth{
  override def sound() ={
    println("animal sound")
  }
}
class Bird extends Animal{
  override def sound()={
    println("bird sounds")
  }
}

// 协变
println("+T 协变测试")
// 定义一个协变容器类Container
class Space[+T]{
  println("hello space")
}
var a =new Space[Animal]
//上面一句 定义了变量a 是Space[Animal] 类型
//下面一句 赋值一个Animal子类的过去 也成功 代码 +T的意思是子类可以赋给父类变量
a = new Space[Bird]
// 因为Earth是Animal的父类 所以下面这句不通过
// a = new Space[Earth]

// 逆变
println("-T 逆变测试")
// 定义一个逆变容器类Container
class Space1[-T]{
  println("hello space")
}
// 完全是上面的反方向 想让子类指向父类的引用
// 正常思维 子类是父类的扩展 子类有的父类不一定有
// 所以把一个父类实例赋给子类实例 调用没有的函数时 会发生错误
// 暂时不明白这样设计的好处是什么 = =
var b =new Space1[Animal]
b = new Space1[Earth]
// 因为Bird是其子类 不是父类 所以编译不通过

// b = new Space1[Bird]

22
Nov
0

maven配置多个配置文件

使用打包命令或是直接在maven的选择框profiles里选择使用哪个环境

开发环境打包命令
mvn clean install -P dev

测试环境打包命令
mvn clean install -P test

生产环境打包命令
mvn clean install -P formal

pom.xml配置
根节点:
<profiles>

<profile>
  <id>test</id>
  <activation>
    <activeByDefault>true</activeByDefault>
  </activation>
  <properties>
    <build.profile.id>test</build.profile.id>
  </properties>
</profile>
<profile>
  <id>develop</id>
  <properties>
    <build.profile.id>develop</build.profile.id>
  </properties>
</profile>
<profile>
  <id>prod</id>
  <properties>
    <build.profile.id>prod</build.profile.id>
  </properties>
</profile>

</profiles>

build节点:
<build>

  <filters>
      <filter>src/app-${build.profile.id}.properties</filter>
  </filters>
  <resources>
      <resource>
          <filtering>true</filtering>
          <!--加上filter会过滤该资源路径中的文件-->
          <directory>${project.basedir}/src/main/resources</directory>
      </resource>
  </resources>

</build>

配置文件Resources目录下:

System.properties 配置文件
app.mode=${app.mode}
app-develop.properties 开发配置文件
app.mode=develop
app-formal.properties 生产配置文件
app.mode=formal
app.test.properties 测试配置文件
app.mode=test

程序中获取:
import java.util.Properties
import java.io.FileInputStream

def loadProperties():Unit = {

val properties = new Properties()
val path = Thread.currentThread().getContextClassLoader.getResource("System.properties").getPath //文件要放到resource文件夹下
properties.load(new FileInputStream(path))
println(properties.getProperty("app.mode"))//读取键为ddd的数据的值
println(properties.getProperty("app.mode","没有值"))//如果ddd不存在,则返回第二个参数
properties.setProperty("app.mode","123")//添加或修改属性值

}