值传递 or 引用传递之 java 与 kotlin 传参对比

1. Java

先说结论:

  • 对于 8 个基本类型 boolean, char, byte, short, int, long, float, double, 方法传递的是一份复制的,怎么操作都不影响原来的值。
  • 对于对象类型或叫类类型(引用类型),方法传递的是一份复制的引用地址(可以理解成创建了一个相同类型的引用,两个引用指向同一个堆里面的对象),那么在方法里面可以通过这个引用地址改变原来对象里面的变量,但无论如何都无法改变原来对象的引用。如果参数没有加final关键字,我们可以改变这个复制过的地址,同样无法改变原来对象的引用,看 1.2 了解更多

1.1 基本类型 的 值传递

private static void passByValue(int i) {
    i = -1;
}

private static void passByReference(Integer i) {
    i = -1;
}
{
    int a = 10;
    Integer b = 200;
    System.out.println("a= " + a);
    System.out.println("b= " + b);
    passByValue(a);
    System.out.println("after passByValue a= " + a);    // 10
    passByReference(a);
    System.out.println("after passByReference a= " + a);    // 10
    passByValue(b);
    System.out.println("after passByValue b= " + b);    // 200
    passByReference(b);
    System.out.println("after passByReference b= " + b);    // 200
}

这里对于Integer b来说,即使调用 passByReference(Integer i), b 的值也不会改变,因为方法里改变的是复制过的引用地址所指向的值。

1.2 类类型 的 引用传递

private static void changeAndAppend(StringBuilder stringBuilder){
    stringBuilder = new StringBuilder("kotlin");
    stringBuilder.append("xiang");
}

private static void appendStringBuilder(StringBuilder stringBuilder) {
    stringBuilder.append("xiang");
}

private static void changeStringBuilder(StringBuilder stringBuilder) {
    stringBuilder = new StringBuilder("kotlin");
}
{
    StringBuilder stringBuilder = new StringBuilder("java");
    System.out.println(stringBuilder.toString());
    changeStringBuilder(stringBuilder);
    System.out.println("after changeStringBuilder stringBuilder= " + stringBuilder.toString());    // java

    StringBuilder stringBuilder2 = new StringBuilder("java");
    System.out.println(stringBuilder2.toString());
    appendStringBuilder(stringBuilder2);
    System.out.println("after appendStringBuilder stringBuilder2= " + stringBuilder2.toString());    // javaxiang

    StringBuilder stringBuilder3 = new StringBuilder("java");
    System.out.println(stringBuilder3.toString());
    changeAndAppend(stringBuilder3);
    System.out.println("after changeAndAppend stringBuilder3= " + stringBuilder3.toString());    // java
}

通过这个例子我们会发现:

  • 在方法changeAndAppendchangeStringBuilder里改变引用并不会改变原先 stringBuilder 和 stringBuilder3,因为传递过去的只是一个副本。
  • appendStringBuilder方法可以通过改变原先对象里的值,因为传递过去的副本和原先的引用指向同一个对象。

2. Kotlin 万物皆对象

直接上结论:

  • Kotlin 里面均是把原先对象的地址传过去,因为所有 kotlin 的方法都不可以改变参数(相当于 java 的final XX),所以方法里面只能通引用来改变对象里的值。
  • 如果引用对象没有提供可以修改它的值的方法,那么我们在方法中只能获取对象的值,而无法对其操作。
fun main() {
    val stringBuilder = StringBuilder("kotlin")
    println(stringBuilder.toString())   // kotlin
    changeStringBuilder(stringBuilder)
    println(stringBuilder.toString())   //kotlin111
}

fun changeInt(a: Int) {
    // a = 2 // val cannot be reassigned
}

fun changeStringBuilder(stringBuilder: StringBuilder) {
    // stringBuilder = StringBuilder("dd") // val cannot be reassigned
    stringBuilder.append("111")
}