一位Java工程师对Swift、Kotlin和Java进行了比较
我们在公司内举办了Kotlin学习会,经常会有人提出疑问,如果用Swift或Java的语法来写Kotlin代码会怎样呢?因此,我打算进行调查并实际实现来解答这个问题。
由于这些语言在语法上有所不同,所以可能不会完全一致,请大家谅解。
※我会定期更新。
验证参考转交
Kotlin:只需要一个选项,用中文进行释义
由于在公司的学习会上谈到了在Kotlin中如何实现传递引用,我进行了一些简单的验证。
在Kotlin中,如果不明确声明为可变(mutable),就无法以引用方式使用。而且,如果尝试对参数进行赋值,将会导致编译错误。
fun addArray(array: MutableList<Int>) {
array.add(9)
println(array) // [1, 2, 3, 9]
}
fun changeArray(array: MutableList<Int>) {
array = mutableListOf(1, 2, 3) // 引数に代入はできない。コンパイルエラーになる。
println(array)
}
fun main(args: Array<String>) {
val array = mutableListOf(1, 2, 3)
println(array) // [1, 2, 3]
addArray(array)
println(array) // [1, 2, 3, 9]
}
迅速
如果在Swift中使用inout声明为引用传递,似乎就可以实现。由于是引用传递,所以可以在函数内对数组变量进行修改。
func addArray(array : inout [Int]) {
array.append(9)
print(array) // [1, 2, 3, 9]
}
func substituteArray(array : inout [Int]) {
array = [100, 200, 300]
print(array) // [100, 200, 300]
}
var array = [1, 2, 3]
print(array) // [1, 2, 3]
addArray(array: &array)
print(array) // [1, 2, 3, 9]
substituteArray(array: &array)
print(array) // [100, 200, 300]
Java – 程序设计语言
我在Java中尝试了相同的组合,虽然可以使用数组的方法来进行更改,但是即使在参考方法内新实例化并赋值,原始变量指向的数组仍然没有改变。
import java.util.ArrayList;
class ArrayTest {
private static void addArray(ArrayList<Integer> array) {
array.add(9);
System.out.println(array); // [1, 2, 3, 9]
}
private static void substituteArray(ArrayList<Integer> array) {
array = new ArrayList<Integer>(); // ここで新しいArrayListを代入しても元のスコープに変更はない。
array.add(100);
array.add(200);
array.add(300);
System.out.println(array); // [100, 200, 300]
}
public static void main(String args[]) {
ArrayList<Integer> array = new ArrayList<Integer>();
array.add(1);
array.add(2);
array.add(3);
System.out.println(array); // [1, 2, 3]
addArray(array);
System.out.println(array); // [1, 2, 3, 9]
substituteArray(array);
System.out.println(array); // [1, 2, 3, 9]([100, 200, 300]にはならない)
}
}
界面
Kotlin 可以。
我本以为Kotlin接口的使用方法与Java没有太大差异,但我认为与Java不同的地方是可以对字段变量进行限制。
interface Lang {
val name: String
fun getHelloWorld(target: String) : String
}
class English : Lang {
override val name = "English" // 宣言しないとコンパイルエラー
override fun getHelloWorld(target: String) : String {
return "Hello, $target!"
}
}
class Japanese : Lang {
override val name = "日本語"
override fun getHelloWorld(target: String) : String {
return "こんにちは、 $target!"
}
}
fun main(args: Array<String>) {
val Elang = English()
println(Elang.getHelloWorld("world")) // Hello, world!
val Jlang = Japanese()
println(Jlang.getHelloWorld("world")) // こんにちは、 world!
}
迅速
在Swift中,可以通过协议实现类似于Kotlin接口的功能。
protocol Lang {
var name : String {get}
func getHelloWorld(target :String) -> String
}
class English: Lang {
let name = "English"
func getHelloWorld(target : String) -> String {
return "Hello, \(target)"
}
}
class Japanese: Lang {
let name = "Japanese"
func getHelloWorld(target : String) -> String {
return "こんにちは, \(target)"
}
}
let Elang = English()
print(Elang.getHelloWorld(target: "world")) // Hello, world!
let Jlang = Japanese()
print(Jlang.getHelloWorld(target: "world")) // こんにちは, world
Java – Java
因为我从来没有考虑过这种用法,所以没有注意到,但是Java在接口中定义字段后,不能强制实现类也拥有相同的字段。
(实际上,这些字段会被强制变为public static final,只能当作常量处理。)
interface Lang {
public static final String name = ""; // インタフェースに定義したフィールドは強制的に public staic final になる。
public String getHelloWorld(String target);
}
class English implements Lang {
// public String name; // 宣言しなくてもOK
@Override
public String getHelloWorld(String target) {
return "Hello, %TARGET%!".replace("%TARGET%", target);
}
}
class Japanese implements Lang {
@Override
public String getHelloWorld(String target) {
return "こんにちわ, %TARGET%!".replace("%TARGET%", target);
}
}
class InterfaceTest {
public static void main(String args[]) {
English Elang = new English();
System.out.println(Elang.getHelloWorld("world")); // Hello, world!
Japanese Jlang = new Japanese();
System.out.println(Jlang.getHelloWorld("world")); // こんにちは, world
}
}
伴侣对象 lǚ
Kotlin
Kotlin is a programming language.
在Java和其他语言中,称为静态访问(Static)的概念,在Kotlin中可以通过Companion Object实现。
interface Lang {
val name: String
fun getHelloWorld(target: String) : String
}
class English : Lang {
override val name = "English"
override fun getHelloWorld(target: String) : String {
return "Hello, $target!"
}
companion object Factory { // Companion object
fun create(): English = English()
}
}
fun main(args: Array<String>) {
val Egreeter = English.create() // CreateメソッドでEnglishの実体を生成
println(Egreeter.getHelloWorld("world"))
}
我尝试了很多方法,但是CompanionObject的操作如下所示。
-
- 1クラス内に定義できるCompanionObjectは一つ
-
- 静的アクセスなので、CompanionObject内から所属クラスのフィールド、メソッドにはアクセスできない。
- const を付けて定数を定義できるし、companion object内に変数を宣言することもできる。
class English : Lang {
override val name = "English"
override fun getHelloWorld(target: String) : String {
return "Hello, $target!"
}
companion object Factory { // Factoryはあってもなくてもいいけど、javaからcompanion object内にアクセスするのに必要。
const val COMPANION_NAME = "Companion" // 定数の定義ができる。
val companionName = "test" // constを付けなくても静的アクセスできるフィールド変数を宣言できる。
fun create(): English = English()
fun getNameCom(): String {
// return name // Companion Object 内から Englishのフィールドにはアクセスできない。
return companionName // Companion Object内の変数ならアクセスできる
}
}
}
从表面上看,它似乎建议在基本上是常量值,或者在实现类似于create方法的工厂模式时使用。
迅速
在Swift中,可以通过静态声明来实现类似的功能。
相比于记作Companion Object,更容易理解。
protocol Lang {
var name : String {get}
func getHelloWorld(target :String) -> String
}
class English: Lang {
let name = "English"
func getHelloWorld(target : String) -> String {
return "Hello, \(target)"
}
static var COMPANION_NAME = "Companion"
static func create() -> English {
return English()
}
static func getName() -> String {
return name // Staticな関数からはメンバ変数にはアクセスできない。
}
}
let Egreeter = English.create()
print(Egreeter.getHelloWorld(target: "world"))
print(English.COMPANION_NAME) // 定数なのでアクセスできる
Java:
JAVA
Java可以像Swift一样进行编程。
interface Lang {
public String getHelloWorld(String target);
}
class English implements Lang {
public String name = "English";
@Override
public String getHelloWorld(String target) {
return "Hello, %TARGET%!".replace("%TARGET%", target);
}
public static final String COMPANION_NAME = "Companion";
public static English create(){
return new English();
}
public static String getName() {
return name; // staticでない変数 nameをstaticコンテキストから参照することはできません エラーが出る。
}
}
class StaticTest {
public static void main(String args[]) {
English Elang = English.create();
System.out.println(Elang.getHelloWorld("world"));
System.out.println(English.COMPANION_NAME);
}
}
类继承和构造函数。
Kotlin只需要一种选项。
为了调查类的继承和构造方法,我创建了一个名为England的类和它的抽象类Country。它们具有属性:国家名称、语言和人口。
此外,国家可以获取问候语和人口数量,实际处理是委托给Lang接口的实现类。
(说实话,我觉得类的设计不是很好。)
interface Lang {
val name: String
fun getHelloWorld(target: String) : String
fun getPopulation(countryName: String, population : Int) : String
}
class English : Lang {
override val name = "English"
override fun getHelloWorld(target: String) : String {
return "Hello, $target!"
}
override fun getPopulation(countryName: String, population : Int) : String {
return "$countryName has a population of $population people."
}
}
// 基底クラスに国名、言語、人口の属性を持たせて、必須になるようにする
abstract class Country (_name : String, _lang: Lang, _population: Int) {
val name : String = _name
val lang : Lang = _lang
val population = _population
fun getPopulation() : String {
return lang.getPopulation(name, population)
}
}
class England() : Country("England", English(), 53013000)
fun main(args: Array<String>) {
val country = England()
println(country.name)
println(country.lang.getHelloWorld("world"))
println(country.getPopulation())
}
迅速
我尝试使用Swift进行了重写,但是Swift没有像抽象类那样的概念,所以我觉得没有太大的必要去实现Country类。
使用协议作为类型也不被推荐(或者说不可行?),所以我还是决定不强制在父类中添加lang属性。
import Foundation
protocol Lang {
var name : String {get}
func getHelloWorld(target :String) -> String
func getPopulation(countryName: String, population : Int) -> String
}
class English: Lang {
let name = "English"
func getHelloWorld(target : String) -> String {
return "Hello, \(target)"
}
func getPopulation(countryName: String, population : Int) -> String {
return "\(countryName) has a population of \(population) people."
}
}
class Country {
var name : String = ""
var population : Int = 0
init(_name : String, _population : Int) {
name = _name
population = _population
}
}
class England : Country {
var lang = English()
init() {
super.init(_name: "England", _population: 53013000)
}
func getPopulation() -> String {
return self.lang.getPopulation(countryName: name, population: population)
}
func getHelloWorld(target: String) -> String {
return self.lang.getHelloWorld(target: target)
}
}
var country = England()
print(country.name)
print(country.getHelloWorld(target: "world"))
print(country.getPopulation())
Java
基本上與Kotlin相同,但是為了符合Java的慣例,將屬性訪問轉換為getter。因此,從代碼上來看,相對於Kotlin而言,它可能會變得更長。
interface Lang {
public String getHelloWorld(String target);
public String getPopulation(String countryName, int population);
}
class English implements Lang {
private String name = "English";
public String getName() {
return name;
}
@Override
public String getHelloWorld(String target) {
return "Hello, %TARGET%!".replace("%TARGET%", target);
}
@Override
public String getPopulation(String countryName, int population) {
return "%COUNTRY_NAME% has a population of %POPULATION% people."
.replace("%COUNTRY_NAME%", countryName)
.replace("%POPULATION%", Integer.toString(population));
}
}
public abstract class Country {
private String name;
private Lang lang;
private int population;
public Country(String _name, Lang _lang, int _population) {
name = _name;
lang = _lang;
population = _population;
}
public String getPopulation() {
return lang.getPopulation(name, population);
}
public String getName() {
return name;
}
public Lang getLang() {
return lang;
}
}
public class England extends Country {
public static final String NAME = "England";
public static final int POPULATION = 53013000;
public England() {
super(NAME, new English(), POPULATION);
}
}
class ConstructorTest {
public static void main(String args[]) {
England country = new England();
System.out.println(country.getName());
System.out.println(country.getLang().getHelloWorld("world"));
System.out.println(country.getPopulation());
}
}
空值安全
我将要写。