JGSK - 05.数据类型

Table Of Contents

Java 篇

Java 的数据类型主要分为基本数据类型和引用数据类型。

基本类型

Java 的基本类型有 int, long, float, double, char, boolean,即以小写字母开头的数据类型。

int x = 2;
float y = 0.1f;
boolean flag = false;

BigDecimal

Java 的基本数值类型及其包装类在进行浮点操作时,很有可能会损失精度。为此,Java 引进了 BigDecimal 类,可以在不损失精度的情况下进行计算。但是不幸的是 Java 的 BigDeciaml 的构造器设计得非常反人类。

System.out.println(2.0 - 1.8);	//0.19999999999999996
System.out.println(new BigDecimal(2.0).subtract(new BigDecimal(1.8)));	//0.1999999999999999555910790149937383830547332763671875
System.out.println(new BigDecimal("2.0").subtract(new BigDecimal("1.8")));	//0.2

从上面的例子可以看到,BigDecimal 有字符串和数值两种构造器,字符串构造器构造的 BigDecimal 才能满足我们的需求,而不是直接使用数值进行构造,基本上用过这个类的人都会踩过这个坑。

引用数据类型

Java 中除基本数据类型之外的所有数据都属于引用数据类型,且所有引用数据类型都是 Object 类的子类。

类型转换

隐式转换

所谓的隐式转换即无需显示指明需要进行类型转换,由编译器根据变量的类型自动推断。

Java 的隐式转换只发生在使用字面值给变量赋值或者向上转型时,如以下例子

byte b = 1;
int i = b;	//	byte 类型向上转型为 int 类型
char c = 1;	//	使用 int 型的字面值 1 为 char 变量赋值

如上述的例子,Java 默认数值字面值为 int 型,只要该数值在 byte 取值范围内,字面值就可以直接赋值给 byte 类型变量。此外由于 int 类型取值范围比 byte 大,所以 byte 变量可以直接赋值给 int 变量,即向上转型。

显式转换

无论是基本类型还是引用类型都是使用 (dataType)value 的语法进行转换。显示转换又称作强制转换,且向下转型时很可能会丢失精度。

int i = (int) 99.98

类型推断

Java 使用关键字 isInstanceof 来判断变量的类型

例:

private static void bar(Object foo) {
    if (foo instanceof String) {
        String fooString = (String) foo;
        System.out.println(fooString.toUpperCase());
    }
}

Object foo = "foo";
bar(foo);

从以上例子可以看到,”foo” 已经通过类型判断知道是 String 类型了,但是还需要进行强制类型转换,并且再声明一个变量,这一做法稍显多余。稍后可以看到 Groovy 的做法要优雅得多。

Groovy 篇

由于 Groovy 就是 Java,所以 Groovy 的数据类型也与 Java 完全一样。但是 Groovy 根据情况的不同会对数据进行包装。

int i = 1
1.toString()

以上第一行在编译后的结果与 Java 完全一样,而第二行由于调用了方法,所以 Groovy 会将其转换为 Integer 类型。这一自动化操作省去了在写 Java 时的不少麻烦。

所以可以认为 Groovy 中一切皆对象。

Groovy 通常将数据类型分为静态类型和动态类型。

静态类型

静态类型与 Java 的声明方式完全一样,声明后就不可以再改变数据类型。

int x = 2
float y = 0.1f
boolean flag = false

动态类型

Groovy 使用关键字 def 声明动态类型变量,与静态类型变量不一样,动态类型变量在定义后可以任意改变类型,类似 Javascript。

定义动态类型变量

def dx = 2
def dy = 0.1
def dflag = false

改变类型

Date staticDate = new Date()
// 静态类型变量无法改变数据类型,会报 'java.lang.Integer' to class 'java.util.Date'
//        staticDate = 2

// 可以由 Date 类型转换为 Integer 类型
def dynamicDate = new Date()
dynamicDate = 2

BigDecimal

与 Java 不同,Groovy 中定义一个浮点类型数据实际就是使用 BigDecimal 的字符串构造器定义一个 BigDecimal,所以 Groovy 中不需要做任何额外操作直接就可以做精确的浮点运算。

println(2.0 - 1.8);	//0.2

类型转换

隐式转换

同 Java

显式转换

Groovy 使用方法来实现基本类型的显式转换,语法看起来更加优雅

int i = 99.98.toInteger()
"99.12".toDouble()

但是 Groovy 并没有提供到 “char” 类型的转换方法,所以 “char” 类型还是需要使用 Java 样式的转换方式或者使用另一种方式的类型转换:

char c = 99 as char

对于引用类型的话则转换方式则类似 Java。

类型推断

对于引用类型而言,Groovy 也使用关键字 instanceof 做类型推断,但是 Groovy 也提供了 isXXX() 的语法糖来进行基本类型的推断。

可以看到,通过 instanceof 进行判断以后,变量就可以直接调用其真实类型的方法,无需再声明另一个变量并进行强制类型转换,这一做法相比 Java 方便很多。但是如果使用的是 isXXX() 的话则不支持此特性。

def static bar(foo) {
    if (foo instanceof String) {
        println(foo.toUpperCase())
    }
    if (foo instanceof Double) {
        println(foo.intValue())
    }
    if (foo.isDouble()) {
        println(foo.toDouble())
    }
}

def foo = "foo"
bar(foo)

Scala 篇

Scala 中并不存在基本类型,所有数据都是对象,且都继承自 Any 类。Scala 类型主要分为 AnyValAnyRef,两者都是 Any 的子类。

AnyVal

AnyVal 分为 Int, Long, Float, Double, Boolean, Char, Byte,Unit 几种类型。除了最后一种,其它可以看做是 Java 上的基本类型的包装类。而 Unit 则相对于 Java 平台上的 Void,即表示没有返回值。

var x: Int = 2
var y: Float = 0.1f
var flag: Boolean = false

AnyRef

AnyRef 是 Scala 中所有引用类的基类,本质上就是 Java 中的 Object 类。

BigDecimal

Scala 中的 BigDecimal 虽然没有 Groovy 那么方便,但是也不像 Java 那样反人类。

println(BigDecimal(2) - BigDecimal(1.8))

类型转换

隐式转换

类似 Java

显式转换

对于 AnyVal 类型,Scala 使用方法进行类型转换

var i:Int = 99.98.toInt
"99.12".toDouble

对于 AnyRef类型,Scala 则使用方法 asInstanceOf() 类进行类型转换

val fooString: String = foo.asInstanceOf[String]

类型推断

Scala 使用 isInstanceOf 来进行类型推断,整个过程与 Java 基本一样,需要额外定义变量并显式进行类型转换。

def bar(foo: Any): Unit = {
  if (foo.isInstanceOf[String]) {
    val fooString: String = foo.asInstanceOf[String]
    println(fooString.toUpperCase)
  }
}

Kotlin 篇

Kotlin 中一切皆对象,Any 为所有类的基类。

数据类型

Kotlin 中的所有类型都是引用类型,但是与其它几门语言不一样,Kotlin 中的 Char 类型不属于数值类型。

var x: Int = 2
var y: Float = 0.1f
var flag: Boolean = false

val c: Char = 1	//  错误,Char 不是数值类型

BigDecimal

Kotlin 并没有专门的 BigDecimal 类,需要调用 Java 来完成计算

println(BigDecimal("2").subtract(BigDecimal("1.8")));

类型转换

隐式转换

Kotlin 只支持使用字面值给变量赋值时的隐式转换,不支持自动向上转型,这点上比其它几门语言都要严格不少。

var b: Byte = 1

//  错误,不支持自动向上转型
val i: Int = b

显式转换

Kotlin 也使用方法进行内置类型的转换

val i:Int = 99.98.toInt()
"99.12".toDouble()

对于其它类型的数据,Kotlin 使用 as 关键字进行类型转换。

val fooString: String = foo as String

类型推断

Kotlin 使用关键字 “is” 来进行类型判断,且在类型判断后数据会被隐式转换为真正的类型,所以可以直接调用该类型的所有方法而无需手动强制转换,Kotlin 称这一过程为 “Smart Cast”。

fun bar(foo: Any) {
    if (foo is String) {
        println(foo.toUpperCase())
    }
}

val foo = "foo"
bar(foo)

总结

  • 除了 Java 之外,其它语言都含有一切皆对象这一概念
  • 每种语言都有自己的基类:Java->Object, Grooy->Object, Scala->Any, Kotlin->Any
  • 除了 Java 之外,其它语言都使用方法来完成内置类型的强制类型转换

项目源码见 JGSK/_05_datatype