相对于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理解的更多相关文章

  1. iOS之iOS中的(null)、<null>、 nil 的问题

      摘要: 你有没有过这样的经历,就是界面上显示出类似<null>.(null)这样一些东西,有时候还会莫名其妙的闪退.反反复复真是曰了犬,今天来总结一下这个问题的解决方法 前段时间开发过 ...

  2. IOS开发遇到(null)与<null>轻松处理

    在ios开发中不可避免的我们会遇到服务器返回的值有空值,但是如果是nil也就算了还可能得到(null)以及<null>的返回值,该如何处理呢?(当然有的字典转模型中已处理,可以通过遍历等) ...

  3. IOS开发中(null)与<null>的处理

    不小心在开发过程中,得到了(null)以及<null>的返回值,找了好长时间只找到了一个关于<null>的. 由于要根据返回值进行判断,做出必要反应,因此必须知道返回值所代表的 ...

  4. 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 { ...

  5. ognl.OgnlException: target is null for setProperty(null, "emailTypeNo", [Ljava.lang.String;@1513fd0)

    [com.opensymphony.xwork2.ognl.OgnlValueStack] - Error setting expression 'emaiTypeDto.emailTypeNo' w ...

  6. SQL语句中=null和is null

    平时经常会遇到这两种写法:IS NOT NULL与!=NULL.也经常会遇到数据库有符合条件!=NULL的数据,但是返回为空集合.实际上,是由于对二者使用区别理解不透彻. 默认情况下,推荐使用 IS ...

  7. mysql探究之null与not null

    相信很多用了mysql很久的人,对这两个字段属性的概念还不是很清楚,一般会有以下疑问: 1.我字段类型是not null,为什么我可以插入空值 2.为毛not null的效率比null高 3.判断字段 ...

  8. 【MySQL】探究之null与not null

    相信很多用了mysql很久的人,对这两个字段属性的概念还不是很清楚,一般会有以下疑问: 我字段类型是not null,为什么我可以插入空值 为毛not null的效率比null高 判断字段不为空的时候 ...

  9. 数据库中is null(is not null)与=null(!=null)的区别

    在标准SQL语言(ANIS SQL)SQL-92规定的Null值的比较取值结果都为False,既Null=Null取值也是False.NULL在这里是一种未知值,千变万化的变量,不是“空”这一个定值! ...

随机推荐

  1. C#调用存储过程带输出参数或返回值

    CREATE PROCEDURE [dbo].[GetNameById] @studentid varchar(8), @studentname nvarchar(50) OUTPUT AS BEGI ...

  2. 不使用数据结构反转栈 递归 CVTE实习 CVTE是一家什么公司

    本文因为垃圾csdn标题字限制,标题写不好.本文想说一个算法,和我在CVTE的实习,我看到CVTE是一家什么公司.如果想要喷我的,可以留言,我不会理.如果想喷公司,可以在博客评论或发到我邮件linde ...

  3. Spring装配Bean之XML装配bean

    在Spring刚出现的时候,XML是描述配置的主要方式,在Spring的名义下,我们创建了无数行XML代码.在一定程度上,Spring成为了XML的同义词. 现在随着强大的自动化配置和Java代码的配 ...

  4. 三、Spring的面向切面

    Spring的面向切面 在应用开发中,有很多类似日志.安全和事务管理的功能.这些功能都有一个共同点,那就是很多个对象都需要这些功能.复用这些通用的功能的最简单的方法就是继承或者委托.但是当应用规模达到 ...

  5. C基本类型

    C基本类型有: char:8位,可添加修改符signed或是unsigned short:16位,同有singed和unsigned int:32位,同有singed和unsigned long:在3 ...

  6. 使用EF操作Mysql数据库中文变问号的解决方案

    问题场景:使用Entity Framework 6.0 操作Mysql数据库,中文保存至数据库后全部变成问号.但是使用Mysql API却不会. 原因排查:首先想到的肯定是数据库编码问题,一次查询了表 ...

  7. tomcat启动报错Several ports (8080, 8009) required by Tomcat v6.0

    tomcat启动报错 如下图: 问题:8080.8009端口已经被占用. 解决办法: 1.在命令提示符下,输入netstat -aon | findstr 8080 2.继续输入taskkill -F ...

  8. python 生成器和迭代器

    迭代器协议 1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个Stoplteration异常,以终止迭代(只能往后走不能往前退) 2.可迭代对象:实现了 ...

  9. 事件轮询中的task与microtask

    event loop 网上看到的一篇文章,关于介绍task和Tasks, microtasks, queues and schedules,尝试简单翻译一下写进来吧! 原文地址:https://jak ...

  10. x86-64栈帧中的“红色区域” red zone of stack frame on x86-64

    前几天看System V AMD64 ABI标准的时候发现栈帧的顶部后面有一块"red zone",在学cs:app3e/深入理解操作系统的时候并没有遇到这个,总结一下. 引用标准 ...