当前位置:网站首页>Scala基础【异常、隐式转换、泛型】
Scala基础【异常、隐式转换、泛型】
2022-08-03 02:21:00 【hike76】
文章目录
一 异常
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() )
边栏推荐
猜你喜欢
随机推荐
leetcode:163 缺失的区间
Summary of some interviews
2022-08-01 顾宇佳 学习笔记
ES6 新特性:Class 的基本语法
【云原生】灰度发布、蓝绿发布、滚动发布、灰度发布解释
JVM internal structure and various modules operation mechanism
2022-08-02 顾宇佳 学习笔记 多线程
flask-socketio实现websocket通信
”QSqlDatabasePrivate::removeDatabase: connection ‘test-connect‘ is still in use“数据库多次打开报错
370万欧元!西班牙iPronics加速可重构光子芯片商用
Wei Dongshan Digital Photo Frame Project Learning (5) Transplantation of libjpeg-turbo
The LVS load balancing cluster and the deployment of the LVS - NAT experiment
无法启动服务 错误 193 0xc1
韦东山 数码相框 项目学习(五)libjpeg-turbo的移植
一次偶然的钓鱼文件分析
# RACE32——高级断点的设置和应用
钻石基础知识介绍
南瓜科学新品上线 开辟益智玩具新世界
05-分布式计算框架
【UE4】搭建局域网内VR直播 UE4.27