Kotlin 中双等于和三等于区别

2021-12-17 22:29:06 浏览数 (1)

Java 中的操作符 == 和 equals 的区别

操作符 ==

  • 如果是基本数据类型比较的是值
  • 如果是引用数据类型比较的是地址

操作符 equals

  • 默认情况下在不重写 equals 方法时,等价于 ==,比较的是地址
代码语言:txt复制
public boolean equals(Object obj) {
代码语言:txt复制
    return (this == obj);
代码语言:txt复制
}
  • 重写 equals 方法时,一般用于比较结构是否相等,例如 String
代码语言:txt复制
public boolean equals(Object anObject) {
代码语言:txt复制
    if (this == anObject) {
代码语言:txt复制
        return true;
代码语言:txt复制
    }
代码语言:txt复制
    if (anObject instanceof String) {
代码语言:txt复制
        String anotherString = (String)anObject;
代码语言:txt复制
        int n = value.length;
代码语言:txt复制
        if (n == anotherString.value.length) {
代码语言:txt复制
            char v1[] = value;
代码语言:txt复制
            char v2[] = anotherString.value;
代码语言:txt复制
            int i = 0;
代码语言:txt复制
            while (n-- != 0) {
代码语言:txt复制
                if (v1[i] != v2[i])
代码语言:txt复制
                    return false;
代码语言:txt复制
                i  ;
代码语言:txt复制
            }
代码语言:txt复制
            return true;
代码语言:txt复制
        }
代码语言:txt复制
    }
代码语言:txt复制
    return false;
代码语言:txt复制
}

但是需要注意的是重写 equals 方法时,需要重写 hashCode() 方法,否则无法和 hash 集合类一起正常工作,可以通过快捷键自动生成

equals()hashCode()toString() 等等方法。

  • Mac: Cmd N
  • Win/Linux: Alt Insert

更多 AndroidStudio 快捷键使用技巧查看下列文章:

  • 图解多平台 AndroidStudio 技巧(一)
  • 图解多平台 AndroidStudio 技巧(二)
  • 图解多平台 AndroidStudio 技巧(三)

关于 Java 的操作符介绍就到这里了,接下来重点来分析 Kotlin 中的操作符。

Kotlin 中的操作符 == 和 === 及 equals

Kotlin 提供了两种方式用于对象的比较。

  • 比较对象的结构是否相等( == 或者 equals

Kotlin 中的操作符 == 等价于 equals 用于比较对象的结构是否相等, 很多情况下使用的是 ==,因为对于浮点类型 Float 和

Double,其实现方法 equals 不遵循 IEEE 754 浮点运算标准。

  • 比较对象的引用是否相等 ( === )

Kotlin 中的操作符 === 用于比较对象的引用是否指向同一个对象,运行时如果是基本数据类型 === 等价于 ==

我们知道了基本概念之后,接下来一起来看一下这些操作符( =====equals ),在以下场景中的使用。

  • 基本数据类型
  • 包装类
  • 普通类
  • 数据类

基本数据类型

我们先来看一个例子:

代码语言:txt复制
val a1 = -0
代码语言:txt复制
val a2 = 0
代码语言:txt复制
println(a1 == a2)       // true
代码语言:txt复制
println(a1.equals(a2))  // true
代码语言:txt复制
println(a1 === a2)      // true
代码语言:txt复制
a1 = 100
代码语言:txt复制
a2 = 100
代码语言:txt复制
println(a1 == a2)       // true
代码语言:txt复制
println(a1.equals(a2))  // true
代码语言:txt复制
println(a1 === a2)      // true

运行时,对于基本数据类型 === 等价于 == 比较的是值(即对象的结构是否相等),如果比较基本数据类型时使用

===,编译器就会给出一个警告,不建议使用。

但是 equals 比较特殊, 对于浮点类型 Float 和 Double 却有不同的表现,代码如下所示。

代码语言:txt复制
val a3 = -0f
代码语言:txt复制
val a4 = 0f
代码语言:txt复制
println(a3 == a4)       // true
代码语言:txt复制
println(a3.equals(a4))  // false
代码语言:txt复制
println(a3 === a4)      // true

正如你所看到的 a3.equals(a4) 结果为 false,那么为什么会这样呢,一起来查看反编译后的 Java 代码都做了什么。`Tools →

Kotlin → Show Kotlin Bytecode` 。

代码语言:txt复制
float a3 = -0.0F;
代码语言:txt复制
float a4 = 0.0F;
代码语言:txt复制
boolean var2 = Float.valueOf(a3).equals(a4);
代码语言:txt复制
boolean var3 = false;
代码语言:txt复制
System.out.println(var2);

将 float 转换为包装类型 Float,调用其 equals 方法来进行比较,来看一下 equals 方法。

运行结果正如源码注释高亮部分一样,使用 equals 方法比较 0.0f-0.0f 其结果为 false, 如果使用操作符 ==

结果为 true。

equals 方法中调用了 floatToIntBits 方法,在这个方法中是根据 IEEE 754 浮点算法标准

,返回指定浮点值的表示形式,结果是一个整数,如下所示:

代码语言:txt复制
System.out.println(Float.floatToIntBits(-0f));  // -2147483648
代码语言:txt复制
System.out.println(Float.floatToIntBits(0f));   // 0

正如你所见,Float.floatToIntBits(-0f) 计算出来的结果,是整数的最小值 -2147483648,从结果来看它不遵循 **IEEE

754 浮点运算标准** ,一起来看一下官方是如何解释的,更多信息点击查看 [IEEE 754

浮点运算标准](https://links.jianshu.com/go?to=https://kotlinlang.org/docs/basic-

types.html#unsigned-integers)

对于浮点类型 Float 和 Double,其实现方法 equals 不遵循 IEEE 754 浮点运算标准

  • NaN 被认为和它自身相等
  • NaN 被认为比包括正无穷在内的任何其他元素都大
  • -0.0 小于 0.0

因此在 Kotlin 中如果使用 equals 方法进行比较的时候,需要注意这个情况。

包装类

无论是 Java 还是 Kotlin 每一种基本类型都会对应一个唯一的包装类,只不过它们的区分方式不一样。

基本数据类型

包装类

byte

Byte

short

Short

int

Integer

long

Long

float

Float

double

Double

char

Character

boolean

Boolean

代码语言:txt复制
val a5 = Integer(10)    
代码语言:txt复制
val a6 = Integer(10)
代码语言:txt复制
println(a5 == a6)       // true
代码语言:txt复制
println(a5.equals(a6))  // true
代码语言:txt复制
println(a5 === a6)      // false

因为包装类重写了 equals 方法,所以使用操作符 ==equals 比较的是对象的结构是否相等,所以结果为 true。而操作符

=== 比较的是对象的引用,是否指向同一个对象,因为是不同的对象,所以结果为 false。

普通的类

普通的类其实就是我们自己新建的类,并没有重写 equals 方法,一起来看一下这三种操作符的运行结果。

代码语言:txt复制
class Person1(val name: String, val age: Int)
代码语言:txt复制
val p1 = Person1(name = "hi-dhl", age = 10)
代码语言:txt复制
val p2 = Person1(name = "hi-dhl", age = 10)
代码语言:txt复制
println(p1 == p2)       // false
代码语言:txt复制
println(p1.equals(p2))  // false
代码语言:txt复制
println(p1 === p2)      // false
代码语言:txt复制
println(p1.name == p2.name)         // true
代码语言:txt复制
println(p1.name.equals(p2.name))    // true
代码语言:txt复制
println(p1.name === p2.name)        // true

因为普通的类 Person1 并没有实现 equals 方法,所以使用操作符 ==equals 比较的结果为 false,而 p1 和

p2 是不同的对象所以操作符 === 的结果为 false。

参数 name 是 String 类型,在上文分析过了 String 重写了 equals 方法,操作符 ==equals 比较的结果为

true。而 p1.name === p2.name 结果为 true , 是因为会先去常量池中查找是否存在 "hi-

dhl",如果存在直接返回常量池中的引用。

数据类

最后我们在来看一下这三种操作符在数据类中的表现。

代码语言:txt复制
data class Person2(val name: String, val age: Int)
代码语言:txt复制
val p3 = Person2(name = "ByteCode", age = 10)
代码语言:txt复制
val p4 = Person2(name = "ByteCode", age = 10)
代码语言:txt复制
println(p3 == p4)       // true
代码语言:txt复制
println(p3.equals(p4))  // true
代码语言:txt复制
println(p3 === p4)      // false
代码语言:txt复制
println(p3.name == p4.name)     // true
代码语言:txt复制
println(p3.name.equals(p4.name))// true
代码语言:txt复制
println(p3.name === p4.name)    // true

因为编译器会根据数据类中的参数,自动生成 equalshashCodetoString 等等方法,编译后的代码如下所示。

代码语言:txt复制
public int hashCode() {
代码语言:txt复制
  String var10000 = this.name;
代码语言:txt复制
  return (var10000 != null ? var10000.hashCode() : 0) * 31   Integer.hashCode(this.age);
代码语言:txt复制
}
代码语言:txt复制
public boolean equals(@Nullable Object var1) {
代码语言:txt复制
  if (this != var1) {
代码语言:txt复制
     if (var1 instanceof Person2) {
代码语言:txt复制
        Person2 var2 = (Person2)var1;
代码语言:txt复制
        if (Intrinsics.areEqual(this.name, var2.name) && this.age == var2.age) {
代码语言:txt复制
           return true;
代码语言:txt复制
        }
代码语言:txt复制
     }
代码语言:txt复制
     return false;
代码语言:txt复制
  } else {
代码语言:txt复制
     return true;
代码语言:txt复制
  }
代码语言:txt复制
}

所以使用操作符 ==equals,输出结果为 true,但是 p3 和 p4 是不同的对象所以操作符 === 的结果为 false。

总结

Java 中的操作符

操作符 ==

  • 如果是基本数据类型比较的是值
  • 如果是引用数据类型比较的是地址

操作符 equals

  • 默认情况下在不重写 equals 方法时,等价于 ==,比较的是地址
  • 重写 equals 方法时,常用于比较结构是否相等,可以通过快捷键自动生成 equals()hashCode()toString() 等等方法。
    • Mac: Cmd N
    • Win/Linux: Alt Insert

Kotlin 中的操作符

Kotlin 提供了两种方式用于对象的比较。

  • 比较对象的结构是否相等( == 或者 equals

Kotlin 中的操作符 == 等价于 equals 用于比较对象的结构是否相等, 很多情况下使用的是 ==,因为对于浮点类型 Float 和

Double,其实现方法 equals 不遵循 IEEE 754 浮点运算标准。

  • 比较对象的引用是否相等 ( === )

Kotlin 中的操作符 === 用于比较对象的引用是否指向同一个对象,运行时如果是基本数据类型 === 等价于 ==

全文到这里就结束了,最后附上文章的精简示例,你能够在不运行程序的情况下,说出下面代码的运行结果吗?

代码语言:txt复制
class Person1(val name: String, val age: Int)
代码语言:txt复制
data class Person2(val name: String, val age: Int)
代码语言:txt复制
fun main() {
代码语言:txt复制
    val a1 = -0
代码语言:txt复制
    val a2 = 0
代码语言:txt复制
    println(a1 == a2)
代码语言:txt复制
    println(a1.equals(a2)
代码语言:txt复制
    val a3 = -0f
代码语言:txt复制
    val a4 = 0f
代码语言:txt复制
    println(a3 == a4)
代码语言:txt复制
    println(a3.equals(a4))
代码语言:txt复制
    //-------------
代码语言:txt复制
    val p1 = Person1(name = "hi-dhl", age = 10)
代码语言:txt复制
    val p2 = Person1(name = "hi-dhl", age = 10)
代码语言:txt复制
    println(p1 == p2)
代码语言:txt复制
    println(p1.equals(p2))
代码语言:txt复制
    println(p1 === p2)
代码语言:txt复制
    println(p1.name === p2.name)
代码语言:txt复制
    //-------------
代码语言:txt复制
    val p3 = Person2(name = "ByteCode", age = 10)
代码语言:txt复制
    val p4 = Person2(name = "ByteCode", age = 10)
代码语言:txt复制
    println(p3 == p4)
代码语言:txt复制
    println(p3.equals(p4))
代码语言:txt复制
    println(p3 === p4)
代码语言:txt复制
    println(p3.name === p4.name)
代码语言:txt复制
}

运行结果如下所示:

代码语言:txt复制
a1 == a2        true
代码语言:txt复制
a1.equals(a2)   true
代码语言:txt复制
a3 == a4        true
代码语言:txt复制
a3.equals(a4)   false
代码语言:txt复制
--------------------------
代码语言:txt复制
p1 == p2        false
代码语言:txt复制
p1.equals(p2)   false
代码语言:txt复制
p1 === p2       false
代码语言:txt复制
p1.name === p2.name true
代码语言:txt复制
--------------------------
代码语言:txt复制
p3 == p4        true
代码语言:txt复制
p3.equals(p4)   true
代码语言:txt复制
p3 === p4)      false
代码语言:txt复制
p3.name === p4.name true

<p align="center">

<p align="center"><b>如果有帮助 点个赞 就是对我最大的鼓励</b></p>

<p align="center"><b>代码不止,文章不停</b></p>

</p>

0 人点赞