scala(一)Nothing、Null、Unit、None 、null 、Nil理解
相对于java的类型系统,scala无疑要复杂的多!也正是这复杂多变的类型系统才让OOP和FP完美的融合在了一起!
Nothing:
如果直接在scala-library中搜索Nothing的话是找不到了,只能发现一个Nothing$的类(后面再说Nothing$和Nothing的关系)。要想看到Nothing.scala的源码需要去github上的scala源码中查找Nothing源码 可以看到在Nothing.scala中只是定义了一个sealed trait:
package scala /** `Nothing` is - together with [[scala.Null]] - at the bottom of Scala's type hierarchy.
*
* `Nothing` is a subtype of every other type (including [[scala.Null]]); there exist
* ''no instances'' of this type. Although type `Nothing` is uninhabited, it is
* nevertheless useful in several ways. For instance, the Scala library defines a value
* [[scala.collection.immutable.Nil]] of type `List[Nothing]`. Because lists are covariant in Scala,
* this makes [[scala.collection.immutable.Nil]] an instance of `List[T]`, for any element of type `T`.
*
* Another usage for Nothing is the return type for methods which never return normally.
* One example is method error in [[scala.sys]], which always throws an exception.
*/
sealed trait Nothing
在这里,Nothing没有任何实例,其类似于java中的标示性接口(如:Serializable,用来标识该该类可以进行序列化),只是用来标识一个空类型。根据官方注释可以看出来Nothing用来标识no instances类型,该类型是其它所有类型的子类型。官方注释中给了如下两个用途:
1、用来当做Nil的类型List[Nothing]
case object Nil extends List[Nothing] {...}
Nil表示一个空的list,与list中的元素类型无关,他可以同时表示List[任意类型]的空集合。也即是Nil可以同时表示List[Int]类型的空集合和List[String]类型的空集合。那么考虑一下Nil:List[?],这里的“?”应该为什么类型呢?也即是“?”应该为Int、String....所有类型的子类型(List集合为协变的)。因此这里引入了Nothing类型作为所有类型的子类型。这样Nil:List[Nothing]就可以完美实现上述需求。
2、表示非正常类型的返回值类型
例如Nil中的两个方法:
override def head: Nothing = throw new NoSuchElementException("head of empty list")
override def tail: List[Nothing] = throw new UnsupportedOperationException("tail of empty list")
Nil为空List,所以调用head和tail应该返回Nothing和List[Nothing]的实例。但是Nothing是没有实例的,这里就直接抛出Exception。所以这里就使用Nothig来表示throw .... 非正常返回的类型。非正常即发生了错误没有返回任何对象,连Unit都没有,用Nothing类表示确实也挺合适。
明白了Nothing的表示的含义以及Nothing的应用场景,那么,Nothing是如何工作的呢?Nothing和Nothing$之间又有什么关系呢?
分别对Nothing.scala和Nothing$.scala进行编译和反编译:
Nothing.scala
Nothing$.scala
编译之后的文件名
根据上面的结果来看,Nothing.scala编译之后是一个接口类型:
public abstract interface Nothing {}
而Nothing$.scala编译之后是一个抽象类:
public abstract class Nothing$ extends Throwable{}
从这里看两者没有任何关系。但是在Nothign$.scala的源码中有一段注释:
package scala
package runtime
/**
* Dummy class which exist only to satisfy the JVM. 虚拟类,只存在于JVM中
* It corresponds to `scala.Nothing`. 它对应scala.Nothing
* If such type appears in method signatures, it is erased to this one. 如果该Nothing出现在方法签名中则将会被抹掉,然后替换为Nothing$
*/
sealed abstract class Nothing$ extends Throwable
这里阐明了Nothing$的作用,也即是代码中如果出现Nothing类型的时候,在load到JVM运行的时候将会把Nothing替换为Nothing$类型。也即在JVM之外以Nothing的身份进行显示,在JVM中以Nothing$的身份进行显示,两者表示同一个含义。
这样解释也满足了Nothing可以当做 throw new XXXXXException("head of empty list")的类型使用的原因,即: Nothing$ extends Throwable.
Null:
Null.scala的源码和Nothing.scala的源码在同一个包中存在着Null.scala源码 。对比一下两者:
Null有唯一的实例null Nothing没有任何实例
Null是所有引用类型(AnyRef)的子类型 Nothing是所有类型的子类型 因此=> Nothing是Null 的子类型
除了上面的两点区别之外,Null和Nothing几乎一致
Null.scala 源码:
package scala /** `Null` is - together with [[scala.Nothing]] - at the bottom of the Scala type hierarchy.
*
* `Null` is a subtype of all reference types; its only instance is the `null` reference.
* Since `Null` is not a subtype of value types, `null` is not a member of any such type. For instance,
* it is not possible to assign `null` to a variable of type [[scala.Int]].
*/
sealed trait Null
注释中也明确说明Null是所有引用类型的子类型,只有唯一个一个实例null。trait Null 说明其实一个类型,那么就可以用该类型定义字段:val myNull:Null=null
那么注释中说Null有唯一的一个实例,那么我们new一个Null如何呢?
这里提示Null是一个abstract抽象类,可源码中定义的Null是一个trait,那么这里在运行的时候为何会提示Null is abstract呢?其次在和Nothing$.scala 旁边同样存在一个Null$.scala,这个类和Null.scala又有什么关系呢?
正如你想象的那样,Null$.scala正是Null在JVM中的另一种身份。我们看一下Null$.scala 的源码:
package scala
package runtime
/**
* Dummy class which exist only to satisfy the JVM. 该类为虚拟类,只存在JVM
* It corresponds to `scala.Null`. 它对应着scala.Null
* If such type appears in method signatures, it is erased to this one. 如果Null出现在方法签名中则用Null$去替换
* A private constructor ensures that Java code can't create subclasses. private构造方法确保不会被创建其它实例
* The only value of type Null$ should be null 唯一个实例及时null
*/
sealed abstract class Null$ private ()
这里明确指出Null将会被Null$替换,那么在运行的时候Null便为Null$类型!原来上面提示Null is abstract是这个原因!!我们再进一步确认一下,查看一下Null$.scala 进行编译看编译之后的代码:
对Null$.scala进行编译然后反编译结果:
(注意:在javap查看代码的时候如果是private 构造参数则不会显示处理,如果是public则会直接显示处理,上面没有显示private Null$()正说明Null$的构造参数正是private类型的)
到这里就完全就明白了,Null在运行期间被替换了Null$.
Unit:
在解释Unit之前需要分析一下函数的返回值有几种可能性!
1、正常返回了数据,例如 def demo01():Int=10 def demo02():String="hello"
2、方法发生异常,没有执行结束,未进行返回操作。例如 def demo02():Nothing=throw new NoSuchElementException("head of empty list")
3、方法返回了一个类型X的实例,该类型X表示 “没有返回值”的类型。这里有些绕,要仔细理解“没有返回值”和“没有返回值类型”。而这里的X即是Unit,而返回的那个实例即(),在java中表示未void。例如: def demo03():Unit={println("hello")}
对于1很好理解,返回什么就接受什么类型。对于2便是上面说的Nothing类型,没有进行返回操作;对于3是有返回的,只是返回的内容表示“没有返回值”的类型。
在源码中很好的解释了Unit的作用:
/** `Unit` is a subtype of [[scala.AnyVal]]. There is only one value of type
* `Unit`, `()`, and it is not represented by any object in the underlying
* runtime system. A method with return type `Unit` is analogous to a Java
* method which is declared `void`.
*/
final abstract class Unit private extends AnyVal {
// Provide a more specific return type for Scaladoc
override def getClass(): Class[Unit] = ???
}
根据注释可以看出,Unit是所有AnyVal 的子类(注意区别Nothing),只有一个唯一的Value(注意这里是Value依旧是实例/对象)。如果方法的返回值类型为Unit,则类似于java中void。
在Unit的伴生对象中则揭开了Unit和void的关系:
object Unit extends AnyValCompanion { /** Transform a value type into a boxed reference type.
*
* @param x the Unit to be boxed
* @return a scala.runtime.BoxedUnit offering `x` as its underlying value.
*/
def box(x: Unit): scala.runtime.BoxedUnit = scala.runtime.BoxedUnit.UNIT /** Transform a boxed type into a value type. Note that this
* method is not typesafe: it accepts any Object, but will throw
* an exception if the argument is not a scala.runtime.BoxedUnit.
*
* @param x the scala.runtime.BoxedUnit to be unboxed.
* @throws ClassCastException if the argument is not a scala.runtime.BoxedUnit
* @return the Unit value ()
*/
def unbox(x: java.lang.Object): Unit = x.asInstanceOf[scala.runtime.BoxedUnit] /** The String representation of the scala.Unit companion object. */
override def toString = "object scala.Unit"
}
请注意box()和unbox()方法,该方法是对数值类型进行装箱和拆箱操作,scala中所有的数值型类型类中都有这两种方法,主要用来数值型向java 数值的封装型转化,例如:int->Integer float->Float
那么Unit中的box()和unbox()也是进行拆装箱操作了,我们看到在Unit的unbox中把java.lang.Object类型转换为一个BoxeUnit类型的数据,那么这个BoxedUnit是什么类型呢?我们打开源码看一下:
package scala.runtime;
public final class BoxedUnit implements java.io.Serializable {
private static final long serialVersionUID = 8405543498931817370L;
public final static BoxedUnit UNIT = new BoxedUnit();
public final static Class<Void> TYPE = java.lang.Void.TYPE;
private Object readResolve() { return UNIT; }
private BoxedUnit() { }
public boolean equals(java.lang.Object other) {return this == other;}
public int hashCode() { return 0;}
public String toString() {return "()";}
}
可以看到其TYPE值直接指向了java的void: public final static Class<Void> TYPE = java.lang.Void.TYPE;
看来scala只是使用Unit对java中的void进行了包装,用Unit来表示void的类型,用BoxedUnit的单例对象来表示那个唯一的void,也即是Unit中的“()”。到这里才明白Unit是没有任何实例的,它只是起一个类型的作用,而那个“()”也只是BoxedUnit的单例对象toSting的输出内容而已。
None:
在说None之前需要了解Option类型。Option类型是对值进行封装,根据值是否为null来返回Some(value)或者None:
def apply[A](x: A): Option[A] = if (x == null) None else Some(x)
Option的apply()方法可以返回None/Some可知None或Some必定是Option的子类了。看三者的定义:
sealed abstract class Option[+A] extends Product with Serializable {}//注意sealed 关键字
//class
final case class Some[+A](value: A) extends Option[A] {
def isEmpty = false
def get = value
}
//Object
case object None extends Option[Nothing] {
def isEmpty = true
def get = throw new NoSuchElementException("None.get")
}
Option[+A]前面有sealed 关键字,则Option[+A]的所有子类必须在同一个文件中定义。因此Option只有Some和None两个子类。注意上面对Some和None的定义,Some是Class,而None是Object。class容易理解,是可以new实例的,而Object类型在编译之后构造方法是private,无法在外部生成实例对象,而其中的方法编译之后变为static的静态方法,可以通过类名直接调用。另外,在对Object进行编译的时候会同时生成一个XXXX$.class的文件,该类是一个单例类,内部会构造一个 public static XXXX$ MODULE$; 单例对象。None也同样如此:
因此我们平时所使用的None其实就是这个public static scala.None$ MODULE$; 单例对象。在应用层面上我们只需要知道None就是一个Option[Nothing]类型的对象,调用get()方法将会抛出NoSuchElementException("None.get")异常,其存在的目的是为了表面java中的NullPointerException()的发生。
null:
null 就很容易理解了和java中的null是同一个null。一般在scala中不直接使用null!
Nil:
看一下源码:
case object Nil extends List[Nothing] {....}
根据object便可知Nil是一个单例对象,必定存在一个Nil$.class,在解压的scala-library中找到Nil$.class进行反编译可以找到Nil$的单例对象:
可以看到 class scala.collection.immutable.Nil$ extends scala.collection.immutable.List<scala.runtime.Nothing$> ,说明Nil是List[Nothing]的子类。而在源码中的方法则直接表明了Nil的性质:一个没有元素的List集合:
override def isEmpty = true//集合为空
override def head: Nothing = throw new NoSuchElementException("head of empty list")//抛出NoSuchElementException异常
override def tail: List[Nothing] = throw new UnsupportedOperationException("tail of empty list")//抛出UnsupportedOperationException异常
=========================================
原文链接:scala(一)Nothing、Null、Unit、None 、null 、Nil理解 转载请注明出处!
=========================================
-----end
scala(一)Nothing、Null、Unit、None 、null 、Nil理解的更多相关文章
- iOS之iOS中的(null)、<null>、 nil 的问题
摘要: 你有没有过这样的经历,就是界面上显示出类似<null>.(null)这样一些东西,有时候还会莫名其妙的闪退.反反复复真是曰了犬,今天来总结一下这个问题的解决方法 前段时间开发过 ...
- IOS开发遇到(null)与<null>轻松处理
在ios开发中不可避免的我们会遇到服务器返回的值有空值,但是如果是nil也就算了还可能得到(null)以及<null>的返回值,该如何处理呢?(当然有的字典转模型中已处理,可以通过遍历等) ...
- IOS开发中(null)与<null>的处理
不小心在开发过程中,得到了(null)以及<null>的返回值,找了好长时间只找到了一个关于<null>的. 由于要根据返回值进行判断,做出必要反应,因此必须知道返回值所代表的 ...
- com.opensymphony.xwork2.ognl.OgnlValueStack] - target is null for setProperty(null, "emailTypeNo", [Ljava.lang.String;@6f205e]
情况1,查询结果未转换为与前台交互的实体类DTO 实体类:EmailTypeDto package com.manage.email.dto; public class EmailTypeDto { ...
- ognl.OgnlException: target is null for setProperty(null, "emailTypeNo", [Ljava.lang.String;@1513fd0)
[com.opensymphony.xwork2.ognl.OgnlValueStack] - Error setting expression 'emaiTypeDto.emailTypeNo' w ...
- SQL语句中=null和is null
平时经常会遇到这两种写法:IS NOT NULL与!=NULL.也经常会遇到数据库有符合条件!=NULL的数据,但是返回为空集合.实际上,是由于对二者使用区别理解不透彻. 默认情况下,推荐使用 IS ...
- mysql探究之null与not null
相信很多用了mysql很久的人,对这两个字段属性的概念还不是很清楚,一般会有以下疑问: 1.我字段类型是not null,为什么我可以插入空值 2.为毛not null的效率比null高 3.判断字段 ...
- 【MySQL】探究之null与not null
相信很多用了mysql很久的人,对这两个字段属性的概念还不是很清楚,一般会有以下疑问: 我字段类型是not null,为什么我可以插入空值 为毛not null的效率比null高 判断字段不为空的时候 ...
- 数据库中is null(is not null)与=null(!=null)的区别
在标准SQL语言(ANIS SQL)SQL-92规定的Null值的比较取值结果都为False,既Null=Null取值也是False.NULL在这里是一种未知值,千变万化的变量,不是“空”这一个定值! ...
随机推荐
- epoll模型的使用
1. 创建epoll句柄 int epfd = epoll_create(int size); 该函数生成一个epoll专用的文件描述符.它其实是在内核申请一空间,用来存放你想关注的socket fd ...
- KMP算法的细节问题
preface: 想必,很多人都知道D.E.Knuth与V.R.Pratt和J.H.Morris同时提出所谓的狂拽酷炫屌炸天的KMP算法,在对字符串的匹配(或是字符串的查找)方面表现出比较好的效率,该 ...
- 微信企业号C#开发配置API
微信开发第一步 : 配置API,实现接收消息服务配置 1.在下图界面先填好内容,事件消息处理可自行选择,我这里是没处理的 2.第二步: 使用vs 进行代码的编写,以下是我的代码.LogTextHe ...
- (转)Python爬虫利器一之Requests库的用法
官方文档 以下内容大多来自于官方文档,本文进行了一些修改和总结.要了解更多可以参考 官方文档 安装 利用 pip 安装 $ pip install requests 或者利用 easy_install ...
- win8.1 安装
下载了Windows8.1企业版的iso文件,文件名称:cn_windows_8_1_enterprise_x86_dvd_2791409.iso 下载地址: http://msdn.itellyou ...
- django(注册→登录→主页)增强版
首先准备一张空白的数据表: urls展示: views主要的几个函数以及数据库链接代码:↓ 后端编写结束↑ ↓前端 前端界面:↓ 幸好成功了,接下来看看数据库有没有插入数据.... 这么 ...
- 【转】漫谈linux文件IO--io流程讲的很清楚
[转]漫谈linux文件IO--io流程讲的很清楚 这篇文章写的比较全面,也浅显易懂,备份下.转载自:http://blog.chinaunix.net/uid-27105712-id-3270102 ...
- [Java第一课]环境变量的配置以及eclipse一些常用快捷键
1.环境变量的配置(这里对xp系统电脑来说:) 首先安装jdk软件. 然后在我的电脑(右键)-->属性-->高级-->环境变量-->系统变量(注意)-->新建(新建两个p ...
- 微信小程序---wx.request(OBJECT)
详情 :https://mp.weixin.qq.com/debug/wxadoc/dev/api/network-request.html#wxrequestobject 1: 首先要配置你的域名 ...
- IE页面刷新ocx插件被释放,野指针非阻塞Sleep问题。
做一个视频页面,自动化测试的时候崩溃.排查了半天,才发现虚表为NLL,然后调用的已经释放对象里面的函数. 问题出在哪呢?出在了左边的非阻塞Sleep的地方.对象已经释放掉了,但是好在阻塞循环,调用st ...