Kotlin中数据类中的私有成员变量

5

我是kotlin的新手,当我阅读有关kotlin中数据类的内容时,我发现了以下代码。这基本上是java模型类和kotlin数据类之间的比较,并且在那里写道这两个代码执行相同的任务。

代码1

public class VideoGame {

private String name;
private String publisher;
private int reviewScore;

public VideoGame(String name, String publisher, int reviewScore) {
    this.name = name;
    this.publisher = publisher;
    this.reviewScore = reviewScore;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getPublisher() {
    return publisher;
}

public void setPublisher(String publisher) {
    this.publisher = publisher;
}

public int getReviewScore() {
    return reviewScore;
}

public void setReviewScore(int reviewScore) {
    this.reviewScore = reviewScore;
}}

代码 2

data class VideoGame(val name: String, val publisher: String, var reviewScore: Int)

我的问题是,在数据类中,所有变量都是公共的而不是私有的,任何人都可以通过该类的对象直接访问变量。但在Java代码中,所有变量都是私有的,因此我们必须编写getter和setter来访问这些成员变量,如果它是公共的,则不需要getter和setter。那么这些代码是如何相同的?

3
实际上,Kotlin 版本只是 Java 版本的语法糖。这些后备字段本身是私有的,我很确定。在这里查看:https://kotlinlang.org/docs/reference/properties.html - user
你能否解释一下吗?我是新手,不太理解。 - Abhishek Agarwal
2个回答

14
data class 的主要用途是简化编写 POJOs 的工作。
如果您进入IDE并编写您的 VideoGame 类:
data class VideoGame(val name: String, val publisher: String, var reviewScore: Int)

然后你需要对其进行反编译:

在你的IDE工具栏中选择:工具 > Kotlin > 显示Kotlin字节码 > 反编译

你会得到以下结果:

@Metadata(
   mv = {1, 1, 16},
   bv = {1, 0, 3},
   k = 1,
   d1 = {"\u0000\"\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0002\n\u0002\u0010\b\n\u0002\b\r\n\u0002\u0010\u000b\n\u0002\b\u0004\b\u0086\b\u0018\u00002\u00020\u0001B\u001d\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\u0006\u0010\u0004\u001a\u00020\u0003\u0012\u0006\u0010\u0005\u001a\u00020\u0006¢\u0006\u0002\u0010\u0007J\t\u0010\u000f\u001a\u00020\u0003HÆ\u0003J\t\u0010\u0010\u001a\u00020\u0003HÆ\u0003J\t\u0010\u0011\u001a\u00020\u0006HÆ\u0003J'\u0010\u0012\u001a\u00020\u00002\b\b\u0002\u0010\u0002\u001a\u00020\u00032\b\b\u0002\u0010\u0004\u001a\u00020\u00032\b\b\u0002\u0010\u0005\u001a\u00020\u0006HÆ\u0001J\u0013\u0010\u0013\u001a\u00020\u00142\b\u0010\u0015\u001a\u0004\u0018\u00010\u0001HÖ\u0003J\t\u0010\u0016\u001a\u00020\u0006HÖ\u0001J\t\u0010\u0017\u001a\u00020\u0003HÖ\u0001R\u0011\u0010\u0002\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\b\u0010\tR\u0011\u0010\u0004\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\n\u0010\tR\u001a\u0010\u0005\u001a\u00020\u0006X\u0086\u000e¢\u0006\u000e\n\u0000\u001a\u0004\b\u000b\u0010\f\"\u0004\b\r\u0010\u000e¨\u0006\u0018"},
   d2 = {"Lcom/yourpackage/VideoGame;", "", "name", "", "publisher", "reviewScore", "", "(Ljava/lang/String;Ljava/lang/String;I)V", "getName", "()Ljava/lang/String;", "getPublisher", "getReviewScore", "()I", "setReviewScore", "(I)V", "component1", "component2", "component3", "copy", "equals", "", "other", "hashCode", "toString", "app"}
)
public final class VideoGame {
   @NotNull
   private final String name;
   @NotNull
   private final String publisher;
   private int reviewScore;

   @NotNull
   public final String getName() {
      return this.name;
   }

   @NotNull
   public final String getPublisher() {
      return this.publisher;
   }

   public final int getReviewScore() {
      return this.reviewScore;
   }

   public final void setReviewScore(int var1) {
      this.reviewScore = var1;
   }

   public VideoGame(@NotNull String name, @NotNull String publisher, int reviewScore) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      Intrinsics.checkParameterIsNotNull(publisher, "publisher");
      super();
      this.name = name;
      this.publisher = publisher;
      this.reviewScore = reviewScore;
   }

   @NotNull
   public final String component1() {
      return this.name;
   }

   @NotNull
   public final String component2() {
      return this.publisher;
   }

   public final int component3() {
      return this.reviewScore;
   }

   @NotNull
   public final VideoGame copy(@NotNull String name, @NotNull String publisher, int reviewScore) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      Intrinsics.checkParameterIsNotNull(publisher, "publisher");
      return new VideoGame(name, publisher, reviewScore);
   }

   // $FF: synthetic method
   public static VideoGame copy$default(VideoGame var0, String var1, String var2, int var3, int var4, Object var5) {
      if ((var4 & 1) != 0) {
         var1 = var0.name;
      }

      if ((var4 & 2) != 0) {
         var2 = var0.publisher;
      }

      if ((var4 & 4) != 0) {
         var3 = var0.reviewScore;
      }

      return var0.copy(var1, var2, var3);
   }

   @NotNull
   public String toString() {
      return "VideoGame(name=" + this.name + ", publisher=" + this.publisher + ", reviewScore=" + this.reviewScore + ")";
   }

   public int hashCode() {
      String var10000 = this.name;
      int var1 = (var10000 != null ? var10000.hashCode() : 0) * 31;
      String var10001 = this.publisher;
      return (var1 + (var10001 != null ? var10001.hashCode() : 0)) * 31 + this.reviewScore;
   }

   public boolean equals(@Nullable Object var1) {
      if (this != var1) {
         if (var1 instanceof VideoGame) {
            VideoGame var2 = (VideoGame)var1;
            if (Intrinsics.areEqual(this.name, var2.name) && Intrinsics.areEqual(this.publisher, var2.publisher) && this.reviewScore == var2.reviewScore) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }
   }
}

因此,后备字段是私有的。Kotlin为您完成了所有繁琐和繁重的工作。即:
  1. 带有getter和setter的后备字段
  2. toString的基本实现
  3. equals的实现
  4. hashCode的实现
  5. 注释可为空性
  6. 编写构造函数实现
  7. 添加一个有用的copy方法
您可以获得 82 行臃肿的代码,而只需使用 1 个关键字就能完成这些工作,否则您可能会在 Java 中编写这些代码(也许还会引入一些错误)。

快速回顾

  • data class使编写 POJOs 更加容易
  • val告诉编译器为给定属性实现 getter。该属性在类本身内部也将是不可变的。(您不能在属于该类的函数中更改它)
  • var告诉编译器提供 setter 和 getter
  • 您可以在同一个 data class 中设置属性的可见性,例如:data class MyClass(private val prop: Int)

进一步的示例:

您在 val 和 var 前面使用的关键字仅影响 getter 和 setter 的可见性。

例如:公共 get 但私有 set:

class VideoGame {
      var pegiRating: Int = 0
           private set
}

首先感谢您的解释。我想知道当我们只是写val prop:Int时,它被视为私有字段,那么private val prop:Int是什么意思,因为prop已经是私有的了,那么为什么我们还要再次写private关键字? 首先感謝您的解釋。我想知道當我們只寫val prop:Int時,它被視為私有字段,那麼private val prop:Int是什麼意思,因為prop已經是私有的了,那麼為什麼我們還要再次寫private關鍵字? - Abhishek Agarwal
如果生成了getter和setter,您正在定义它们的可见性。您可能希望有一个私有的setter但是公共的getter。让我再举一个例子。 - Some random IT boy
这就是你要的。 - Some random IT boy
非常感谢您的出色解释,它对我帮助很大。 - Abhishek Agarwal
很高兴能帮助您! - Some random IT boy

0

它们并不执行相同的任务,因为Java版本有getter方法而Kotlin版本没有,因为这些属性是val而不是var。如果它们是var,那么所有功能都将存在(此外,Kotlin数据类还具有copy功能和已为您实现的equalshashcodetoString)。

默认情况下(没有自定义getter/setter),一个 Kotlin 属性(公共或非公共)就像一个 Java 私有字段带有(公共或非公共)getter和/或setter。

在Java中,通常不建议公开字段以便其他对象可以直接修改它们。这不具备未来可扩展性,因为如果你决定在改变值时产生副作用,你必须将该字段更改为私有并添加一个setter。这会破坏任何使用该类的代码。为避免出现这种情况,字段应该是私有的,而getter和setter可以是公开的,但这是非常繁琐的代码。

在 Kotlin 中,您可以将默认属性更改为具有自定义setter的属性,并且这不会破坏使用它的代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接