Scala基础【异常、隐式转换、泛型】

一 异常

1 java中的异常

分为两大类:编译时异常,运行时异常,所有的异常都是在运行时产生的

在编写代码时标红只是为了程序的健壮性,提示开发者代码会有问题,需要想办法针对不同的问题解决它,如果不处理或者处理不了,需要将其抛出去(throws Exception),谁调用这段程序谁去处理,如果最终没人能够处理,那么java会直接崩溃

其中的try catch就是自己先尝试解决一下,加入这段代码程序并不会崩溃,会继续向下执行

异常会按照顺序进行捕捉,所以一般将范围大的异常放到下面

public static void main(String[] args) {
    try {
        int i = 0;
        int j = 10 / i;
        new FileInputStream("xxxx");
    } catch (ArithmeticException e){
        e.printStackTrace();
        System.out.println("算数异常");
    }
    catch (Exception e){
        e.printStackTrace();
    }
    System.out.println("程序会继续执行");
}

执行return语句之前会先判断有没有finally,如果有就执行完finally之后再返回,如果没有直接返回

需要注意

  • return关键字不会马上返回结果
  • 所有的return返回同一个值:临时变量,1+1+1=3

以下代码返回结果为2,注意中间变量_temp_

public static void main(String[] args) {
    int j = test();
    System.out.println(j);
}
public static int test(){
    int i = 0;
    try {
        // _temp = i++      _temp = 0, i = 1
        // return _temp     不会马上执行,有finally,向下执行   
        return i++;
    }finally {
        // _temp = ++i      i = 2, _temp = 2
        // return _temp
        return ++i;
    }
}

如果去掉return ++i;加上++i,返回结果为0

int i = 0;
try {
    // _temp = i++      _temp = 0, i = 1
    // return _temp     不会马上执行,有finally,向下执行
    return i++;
}finally {
    // _x = ++i      i = 2, _x = 2, _temp = 0
    // return _temp
    //return ++i;
    ++i;
}

2 scala中的异常

scala知道人们都不知道异常是什么,所以在scala中异常没有分类,当出现异常时也会添加try catch语句

Scala中的异常不区分所谓的编译时异常和运行时异常,也无需显示抛出方法异常,所以Scala中没有throws关键字

try{
  val i = 0;
  val j = 10 / i;
  new FileInputStream("xxx")
}catch {
  case e: ArithmeticException => println("算数异常")
  case e: Exception => println("其他异常")
}

3 常见问题

(1)ClassCastException

public static void main(String[] args) {
    List list = new ArrayList();
    list.add(1);
    list.add("ad");
    List<User> list1 = list;
    for(User user : list1){
        System.out.println(user);
    }
}

(2)NullPointerException

当调用一个为空对象的成员方法或者成员属性会出现空指针异常

因为增强for循环使用了集合的迭代器,所以会出现空指针异常

public static void main(String[] args) {
    List list = null;
    for (Object obj : list){
        System.out.println(obj);
    }
}

(3)如果Java程序调用scala代码,如何明确异常

增加注解 @throws[Exception]

object Scala02_Exception {
  def main(args: Array[String]): Unit = {
  }
  @throws[Exception]
  def test(): Unit ={
    throw new Exception("aaa")
  }
}
public class TestAnn {
    public static void main(String[] args) {
        Scala02_Exception.test();
    }
}

4 final finalize 和finally

final修饰的属性为不可变变量,不是一个常量,只是约束了一个变量的值在初始化后不能够改变

public final String name;
public String email;
public TestDept(){
    name = "zhangsan";
}

name不赋值的情况下会报错,email在构建对象时系统会进行初始化(null)

GC在垃圾回收时会执行下面的方法,flinalize是析构方法,主要目的是在回收前问一下此对象还需要做什么,告诉垃圾回收器,因为某种原因,这个对象是不能够被回收的,但是在第二次回收时,不会调用这个方法

二 隐式转换

1 介绍

现想要从第三方库中拿到年龄,因为需求更改,第三方库中的年龄Int变为了Double类型

  def main(args: Array[String]): Unit = {
    //val age : Int = thirdPart()
    val age : Int = thirdPart().toInt
  }
  def thirdPart(): Double = {
    20.0
  }
//def thirdPart(): Int = {
// 20
//}

以上代码违背了OCP开发原则

OCP:允许功能扩展,但不能够修改源代码

如果程序编译出错,编译器会尝试在整个的作用域中查找能够让程序编译通过的方式,如果找到,编译器会尝试二次编译,让之前出现错误的代码经过二次编译后能够通过,这个转换的过程看不到,但是存在,称之为隐式转换,使用关键字implicit声明隐式转换方法,慢慢地,这也形成了一种扩展功能的转换机制

隐式转换就是类型的转换

implicit def transform(d : Double): Int ={
  d.toInt
}
def main(args: Array[String]): Unit = {
  val age : Int = thirdPart()
  println(age)
}
def thirdPart(): Double = {
  20.0
}

2 隐式函数

使用implicit声明的函数称为隐式函数

功能的扩展,以下代码只在以new的方式新建对象时可行,也即特质只在new时可用

  def main(args: Array[String]): Unit = {
    val user = new User with UpdateUser
    user.insertUser()
    user.updateUser()
  }
  trait UpdateUser{
    def updateUser(): Unit ={
      println("update User...")
    }
  }
  class User{
    def insertUser(): Unit ={
      println("insert User...")
    }
  }
  def getUser(): User ={
    new User
  }

使用get的方式获取对象,隐式函数的方式扩展功能

def main(args: Array[String]): Unit = {
  //声明方法
  implicit def transform(user: User) : UserExt ={
    new UserExt()
  }
  val user = getUser()
  user.insertUser()
  user.updateUser()
}
//声明类
class UserExt{
  def updateUser(): Unit ={
    println("update User...")
  }
}
trait UpdateUser{
  def updateUser(): Unit ={
    println("update User...")
  }
}
class User{
  def insertUser(): Unit ={
    println("insert User...")
  }
}
def getUser() ={
  new User()
}

3 隐式参数、隐式变量

隐式参数方便修改,解耦合,OCP开发原则

隐式参数不用传递,传递的过程由编译器完成

reg不加括号,找的是隐式变量,加括号是传值

def main(args: Array[String]): Unit = {
  //隐式参数
  def reg(implicit password : String = "000000"): Unit ={
    println("默认密码:" + password)
  }
  reg()
  reg("123123")
  //隐式变量
  implicit val password : String = "111111"
  reg
  
  val list = List(1,3,2,4)
  list.sortBy(num=>num)
  list.sortBy(num=>num)(Ordering.Int.reverse)
}

在同一个作用域中,如果有相同的转换规则的多个数据,会发生错误

4 隐式类

scala中可以将implicit关键字声明在类的前面称为隐式类,其不能为顶级对象

在Scala2.10后提供了隐式类,可以使用implicit声明类,隐式类非常强大,同样可以扩展类的功能,在集合的数据处理中,隐式类发挥了重要的作用。其所带的构造参数有且只能有一个

def main(args: Array[String]): Unit = {
  val user = new User()
  user.insertUser()
  user.updateUser()
}
//将User类转换成UserExt
implicit class UserExt(user: User){
  def updateUser(): Unit ={
    println("update user")
  }
}
class User{
  def insertUser(): Unit ={
    println("insert User")
  }
}

5 隐式机制

所谓的隐式机制,就是一旦出现编译错误时,编译器会从哪些地方查找对应的隐式转换规则

  • 当前代码作用域
  • 当前代码上级作用域
  • 当前类所在的包对象
  • 当前类(对象)的父类(父类)或特质(父特质)或伴生对象
  • 其实最直接的方式就是直接导入

三 泛型

1 java中的泛型

(1)泛型和类型

所谓的类型其实就是对外部的数据做约束,所谓的泛型其实就是对内部的数据做约束

泛型和类型的层次不一样,不能作为整体来考虑

以下代码中T为泛型,Test就是类型

class Test<T>{
    public T t;
}

(2)类型参数

泛型在某些场合中,称为类型参数,用于向类中传递参数

Test<User> userTest = new Test<User>();
User t = userTest.t;

Test userTest1 = new Test();
Object t1 = userTest1.t;

class Test<T>{
    public T t;
}
class User{
}

(3)泛型擦除

泛型只在编译时有效,泛型为了防止类型不一致,对类型做了个约束,将这个操作称为“泛型擦除”

Test<User> userTest = new Test<User>();
System.out.println(userTest);

(4)约束数据类型

泛型主要目的是为了约束内部的数据类型

泛型不对前两行代码起作用,不能对现有数据做处理,因为如果想对集合内部数据做处理,需要在运行时完成,而泛型又只在编译时才有效,所以泛型没有这个能力

第四行代码:打印集合,如果集合内部有对象(数据),会调用数据的toString方法,toString方法来自于Object和User和Emp没有关系,所以不会改变类型,不会约束类型,在当前场合下,类型不起任何作用,所以可以运行

List list = new ArrayList();
list.add(new Emp());
List<User> userList = list;
System.out.println(list);

但是如果约定了泛型,对类型做处理时一定会报错,处理的User类型,但集合内的数据是Emp类型

//ClassCastException
for(User user : userList){

}

(5)父子关系

泛型和类型不是一个层次,类型之间存在父子关系,而泛型之间没有所谓的父子关系

以下代码stringList1报错,将泛型换为Object,stringList报错

public static void main(String[] args) {
    ArrayList<String> stringList = new ArrayList<String>();
    test(stringList);

    ArrayList<Object> stringList1 = new ArrayList<Object>();
    test(stringList1);
}
public static void test(List<String> list){
    System.out.println(list);
}

类型的上下级关系如下

ArrayList<Object> stringList1 = new ArrayList<Object>();
test(stringList1);
ArrayList<Object> stringList2 = new ArrayList<Object>();
test(stringList2);

(6)泛型不可变

childList2,childList3报错

不过在使用时,确定好上下限的前提下,可以改变,详情见2泛型边界

public class Scala03_Generic_Java {
    public static void main(String[] args) {
        ArrayList<Child> childList1 = new ArrayList<Child>();
        //ArrayList<Child> childList2 = new ArrayList<Parent>();
        //ArrayList<Child> childList3 = new ArrayList<SubChild>();
    }
}
class Parent{
}
class Child extends Parent{
}
class SubChild extends Child{
}

(7) 泛型边界

工厂模式:创建的对象具有相同的特征,约定类型时,只需要有相同的特征就可以

在实际环境中,为了使用方便,可以定义泛型的边界,边界就是其上哪里找的问题

生产数据,强调数据的通用性,类型的下限,传递的类型向上找

消费数据,强调功能的完整性,类型的上限,传递的类型向下找

public class Scala04_Generic_Java {
    public static void main(String[] args) {
        //向上找
        Producer<Child> p = new Producer<Child>();
        p.produce(new Message<Child>());
        p.produce(new Message<Parent>());
        //p.produce(new Message<SubChild>());
        
        //向下找
        Consumer<Child> c = new Consumer<Child>();
        Message<? extends Child> message = c.getChlidMessage();
        final Child data = message.data;
    }
}

class Message<T>{
    public T data;
}
//生产数据,下限,向上找
class Producer<A>{
    public void produce( Message<? super A> message){

    }
}
//消费数据,上限,向下找
class Consumer<B>{
    public Message<? extends B> getChlidMessage(){
        return null;
    }
}

2 scala中的泛型

Scala的泛型和Java中的泛型表达的含义都是一样的,对处理的数据类型进行约束,但是Scala提供了更加强大的功能

(1)泛型转换

泛型不可变

scala中的泛型使用中括号,同样也是不可变的

val message1 : Message[Child] = new Message[Child]()
//val message2 : Message[Child] = new Message[Parent]()
//val message3 : Message[Child] = new Message[SubChild]()
泛型协变

泛型和类型不是一个层面上的东西,所以无法联合使用,不方便

如果能将类型和泛型当成一个整体来使用的话,将会十分方便

如果将类型和泛型联合使用,类型相同时,如果泛型存在父子类关系,那么联合的类型也就存在父子关系

这个操作其实就是一种变化,称为“协变”,+T

def main(args: Array[String]): Unit = {

  val message1 : Message[Child] = new Message[Child]()
  //val message2 : Message[Child] = new Message[Parent]()
  val message3 : Message[Child] = new Message[SubChild]()
}

class Message[+T]{}

class Parent {}

class Child extends Parent {}

class SubChild extends Child {}
泛型逆变

如果类型相同,泛型之间如果存在父子关系,那么让联合后的类型存在子父关系,颠倒关系

这个操作其实也是一种变化,称为“逆变”,-T

出现了以下的变化过程

Child(父) — SubChild(子)

MessageChild(子) — MessageSubChild(父)

class Message[-T]{}
val message1 : Message[Child] = new Message[Child]()
val message2 : Message[Child] = new Message[Parent]()
//val message3 : Message[Child] = new Message[SubChild]()

(2)泛型边界

scala中的泛型也存在上限和下限的概念,上限和下限使用的是颜文字

def main(args: Array[String]): Unit = {
  val p = new Producer[Child]
  p.produce(new Message[Child])
  p.produce(new Message[Parent])
  //p.produce(new Message[SubChild])
  
  val c = new Consumer[Child]
  val m: Message[_ <: Child] = c.consumer
  val data: Child = m.data
}

class Message[T] {
  var data : T = _
}

class Parent {}

class Child extends Parent {}

class SubChild extends Child {}

//生产数据,保证通用性,向上找,泛型下限,范围越来越大
class Producer[T]{
  def produce(message: Message[ _ >: T]): Unit ={
  }
}
//消费数据,不缺失功能,向下找,泛型上限,范围越来越小
class Consumer[T]{
  def consumer : Message[_ <: T] = {
    null
  }
}

(3)集合中的泛型

val listNum = List(1,2,3,4)
listNum.reduce(_+_)

reduce方法的定义,是一个生产者,向上找

def reduce[A1 >: A](op: (A1, A1) => A1): A1 = reduceLeft(op)

A的含义,就是数据类型,在这里是Int,

trait TraversableOnce[+A] extends Any with GenTraversableOnce[A] {
  self =>

Parent,Child,SubChild

val list : List[Child] = List(
  new Child(), new Child(), new Child()
)
// 原类型为Child,需要指定大于Child的类型
val parent: Parent = list.reduce[Parent](
  (c1, c2) => c1
)
// 小于Child的类型会编译出错
/**
 * Error:(12, 41) type arguments do not conform to method reduce's type parameter bounds
 * [A1 >: com.hike.bigdata.scala.chapter11.Scala03_Generic.Child]
 */
//    val SubChild: SubChild = list.reduce[SubChild](
//      (c1, c2) => c1
//    )

几乎所有集合的所有方法都存在泛型

fold方法的定义

def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
list.fold[Parent](new Parent())(
  (x,y)=>x
)
//出错  A1 >:
//    list.fold[SubChild](new SubChild())(
//      (x,y)=>x
//    )

foldLeft方法的定义,泛型没有约束

def foldLeft[B](z: B)(@deprecatedName('f) op: (B, A) => B): B = 

测试

list.foldLeft[SubChild](new SubChild){
  (x,y)=>x
}

(4)上下文限定

上下文限定是将泛型和隐式转换的结合产物,以下两者功能相同,使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过implicitly[Ordering[A]]获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误。

// 语法含义:表示在调用f方法时调用一个隐式变量,隐式变量类型为Test,泛型是A
def main(args: Array[String]): Unit = {
    def f[A : Test](a: A) = println(a)
    implicit val test : Test[User] = new Test[User]
    f( new User() )
}
class Test[T] {
}
class Parent {
}
class User extends Parent{
}
class SubUser extends User {
}

隐式转换错误(…),此时A为SubUser,那么就需要找一个Test类型,泛型为SubUser的隐式变量

f( new SubUser() )

修改可执行成功

implicit val test : Test[SubUser] = new Test[SubUser]
f( new SubUser() )

同理

implicit val test : Test[Parent] = new Test[Parent]
f( new Parent() )
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OneTenTwo76

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值