Scala特性一览

Scala特性一览

二月 17, 2017 阅读 787 字数 4380 评论 0 喜欢 2

Scala 和Java的区别

Scala和Java的简单差异

  1. Scala里没有 enum 类型,新增 Enumeration 助手类可以实现相关类型。
  2. Scala中无Java基本类型一切皆对象,Java基本类型在Scala中必须首字母大写。
  3. Scala中无接口关键字,而改为特质,特质类似Java抽象类,可以实现相关方法且允许一个类实现多个特质。
  4. Scala允许对符号进行重载如:+-*%等。
  5. Scala无静态方法或静态字段,可用单例对象Object语法结构来达到相同目的。可以用和类和与类同名的伴生对象达到既有实例对象又有静态方法和字段的目的。
  6. Scala中没有 ++ 操作符。并且Scala中所有操作符都是方法的调用。
  7. Scala中没有 break 关键字。
  8. Scala不支持三元表达式。
  9. Scala没有 switch 语句而改为 match 模式匹配

Scala语法简介

Scala方法声明

def addNumber(x:Int,y:Int):Int = {
    x+y
}

Scala中可以去掉不必要的{}或者()。此方法无参数时可省略()。

如果一个方法没有返回值,则证明它是有副作用的,这时即使该方法无参数,也不该省略()。

在Scala中如果方法无返回值,可使用unit,类似于java中的void。

Scala 一般不需要 return 关键字,方法会默认将最后一条执行语句当成返回值。

apply和unapply方法

如果一个 class 或者是 object 有一个主要的方法,那么与其每次显式的调用这个主要的方法. Scala允许将如下函数调用语法:

f(arg1,arg2,...)

扩展到可以应用于函数外的值。如果f不是函数或者方法,就等同于调用

f.apply(arg1,arg2,...)

如果它出现在赋值语句的左侧则等同于调用

f.update(arg1,arg2,...)

可以将对象中的unapply方法当作是伴生对象中apply方法的反向操作。apply接受构造参数将它变成对象。unapply方法接受一个对象,然后从中提取值。

一般而言,apply方法和unapply方法相反。但这并不是必须的。你可以用提取器从任何类型对象中提取信息。

每一个样例类都自带applyunapply方法

模式匹配和样例类

Scala match 表达式对应于Java的 switch

Option类型

类似于Java8中的 Optional 表示那种可能不存在的值,样例子类 Some 包装了某个值,None 表示没有值。

隐式转换和隐式参数

隐式转换

Scala中的隐式转换函数指的是那种以 implicit 关键字声明的带有单个参数的函数。该函数会被自动应用,将值从一种类型转换成另一种。

class RichFile(val from:File){
    def read = Source.fromFile(from.getPath).mkString
}

implicit def file2RichFile(from:File) = new RichFile(from)

Scala会考虑如下的隐式转换函数:

  1. 位于源或目标类型的伴生对象中的隐式函数。
  2. 位于当前作用域可以以单个标识指代的隐式函数。
import com.yicheng.FractionConversions._

如果不想要某个特定的隐式转换可以如下:

import com.yicheng.FractionConversions.{function2Double => _,_}

去除 function2Double 外的所有成员。

隐式转换规则:

  1. 当表达式类型与预期的类型不同时。
  2. 当对象访问不存在的成员时。
  3. 当对象调用某个方法,而该方法的参数声明与传入参数不匹配时

以下三种情况编译器不会尝试使用隐式转换:

  1. 如果代码能在不使用隐式转换的前提下通过编译,则不会使用隐式转换。
  2. 编译器不会尝试同时执行多个转换。
  3. 存在二义性的转换是错误的。

隐式参数

函数或方法可以带有一个标记为 implicit 的参数列表。这种情况下,编译器将会在如下两个地方查找缺省值,提供给该函数或方法。

  1. 在当前作用域所有可以用单个标识符指代的满足类型要求的 valdef
  2. 与所要求类型相关联的类型的伴生对象。相关联的类型包括所要求类型本身以及它的类型参数。

利用隐式参数进行隐式转换

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

编译器不会接受这个函数,因为它并不知道 ab 属于一个带有 < 操作符的类型。

def smaller[T](a:T,b:T)(implicit order:T => Ordered[T]) = if(a < b) a else b

order 是一个带有单个参数的函数,被打上了 implicit 标签,并且有一个以单个标识符出现的名称。它不仅是一个隐式参数,它还是一个隐式转换。

上下文界定

类型参数可以有 T:M 的上下文界定,其中 M 是另一个泛型类型。它要求作用域存在一个类型为 M[T] 的隐式值。

特质和自身类型

Scala 的特质可以同时拥有抽象方法和具体方法,类可以实现多个特质。比起Java接口,特质和类更为相像。如果特质拥有具体行为,那么当特质改变时,所有混入该特质的类都必须重新编译。可以用with关键字来添加额外的特质。

trait Logger{
    def log(message:String)
}

trait TimestampLogger extends Logger{
   abstract override def log(msg:String) {
           //重写抽象方法
        super.log(new java.util.Date() + " " + msg)
    }
}

如果特质中有相同的方法实现,则方法最终被调用将取决于特质被混入的顺序。

特质中的字段可以是抽象和具体的,如果给出了初始值,那么字段是具体的。对于特质中的每一个具体字段,使用该特质的类都会获得一个字段与之对应。这些字段不是继承的,而只是简单地被加到了子类当中。

特质的构造顺序是类的线性化的反向:(串联去掉重复项,右侧胜出)

class UserAccount extends Account with FileLogger with ShortLogger
  1. UserAccount(超类)
  2. Logger(第一个特质的的父特质)
  3. FileLogger(第一个特质)
  4. ShortLogger(第二个特质)
  5. SavingsAccount(类)

当特质扩展类时,编译器能够保证,所有混入该特质的类都认这个特质作为超类,Scala还有另外一个特性保证这一点:自身类型。

trait LoggedException extends Logged{
    this:Exception =>
        def log(){ log(...)}
}

该特质并不扩展 Exception 类,而是有一个自身类型的 Exception,这意味着它只能被混入Exception子类

自身类型同样可以处理结构类型——这种类型只给出类必须拥有的方法,而不是类的名称。如:

trait LoggedException extends Logged {
    this:{ def getMessage():String } =>
        def log(){log(getMessage)}
}

Scala在编译时要将特质翻译为JVM的类和接口。只有抽象方法的特质被简单地翻译成一个Java接口。如果特质中有具体的方法,Scala会创建出一个伴生类,该类用静态方法存放特质方法。

trait Logger {
    def log(msg:String)
}

public interface Logger {
  void log(String msg);
}

trait ConsoleLogger extends Logger {
    def log(msg:String){
     println(msg);
    }
}

public interface ConsoleLogger extends Logger {
    void log(String msg);
}

public class ConsoleLogger$class(){
    //生成的Java伴生类
    public static void log(ConsoleLogger   self,String msg) {
    println(msg)
    }
}

伴生类中不会有任何字段,特质中的字段对应到接口中的抽象 GetterSetter 方法,当某个类实现该特质时,字段被自动加入:

trait ShortLogger extends Logger {
    val maxLength = 5
}

public interface ShortLogger extends Logger {

public abstract int maxLength();

public abstract void weird_prefix$maxLength_$eq(int);

... ...

}

public ShortLogger$class(){
    public void $init$(ShortLogger self){
    self.weird_prefix$maxLength_$eq(15)
    }
}

包对象

受到Java虚拟机的限制,Scala的包只能包含特质和对象,不能包含函数和变量定义。

在Scala中每个包都可以有一个包对象。需要在父包中定义它,且名称与包一样。如:

package com.yicheng.hta

package object people{
    val name = \"\"
}

存在类型

存在类型是为了和Java的类型通配符兼容。存在类型的定义方式是在
类型表达式之后跟上 fromSome{... ...}
Scala的类型通配符只不过是存在类型的语法糖而已,例如:

Array[_]

Map[_,_]

等同于:

Array[T] formSome { type T }
Map[T,U] formSome { type T ; type U}

Scala注解

不同于Java注解参数类型的限制,Scala注解的参数可以是任何类型。

在Scala中实现注解必须扩展 Annotation 特质。

1、针对Java特性的注解

  • @volatile 注解将字段标记为易失的
  • @transient 注解标识字段是瞬间的

  • @strictfp 注解对应Java中的strictfp修饰符

  • @navite 注解对应c或c++代码中实现的方法

2、标记接口

  • @cloneable 标记被克隆对象
  • @remote 标记远程对象

  • @throws 注解来声明Java受检异常,使Java生成正确的签名

  • @varargs 注解可以从Java调用Scala带有变长参数的方法

  • @BeanProperty 如果给字段加上此注解,编译器会生成JavaBeans风格的 Getter方法Setter方法

3、优化的注解

  • @tailrec 如果Scala编译器无法进行尾递归优化就会报错

  • @elidable 注解为可以在生产环境中移除的方法打上标记

  • @specialized 可以让编译器自动生成打包和解包方法

  • @implicitNotFound 注解用于在隐式参数不存在的时候生成有意义的提示

发表评论

电子邮件地址不会被公开。 必填项已用*标注