【Kotlin】【Java】关于Kotlin和Java的比较备忘录
什么样的文章?
如果用Java来编写的话,那么用Kotlin该怎么写呢?一个关于代码比较的备忘录。
感谢您提供的评论,我根据您的意见进行了修改和扩充,解释了@JvmField和const之间的区别。
最近,我在玩Kotlin,對它產生了興趣。
有很多整理Kotlin語法的文章,
但找不到跟Java代碼進行比較的,所以我會記錄下來。
在工作上,大部分還是用Java,為了避免混淆所以記錄下來的目的。
请写一些基础的东西。
声明变量
操作
Kotlin
Java
定数
修飾子 val 変数名 : 型
修飾子 final 型 変数名
変数
修飾子 var 変数名 : 型
修飾子 型 変数名
メソッド・関数
fun 関数名(変数名: 型…): 戻り値
戻り値 関数名(型 変数名…)
型推論 –> 类型推论
在 Kotlin 中,由于类型推断的存在,可以省略类型而定义变量。
String str = "文字列";
final int value = 0;
var str = "文字列"
val value = 0
字符序列
字符串的拼接
在Kotlin中,可以在字符串字面值中插入表达式。
String name = "Benjamin Franklin";
int age = 20
String text = "Your name is " + name + ". You'll be " + (age + 1) + " years old next year.";
var name = "Benjamin Franklin"
var age = 20
var text = "Your name is $name. You'll be ${age + 1} years old next year."
如果只插入变量名,则可以省略花括号。
原始字符串字面意思是直接使用字符表示的字符串。
用三个双引号括起来的字符串被称为原始字符串字面量,
它们不会转义特殊字符如反斜杠和换行,并保持原样显示。
String add = "IJKL";
String text = "ABCD\n" +
"EFGH\n" +
add + "\n" +
"MNOP";
var add = "IJKL"
var text = """
ABCD
EFGH
$add
MNOP
"""
如果你想要排除缩进等等的影响,
如果不想要反映缩进,可以在行首放置一个竖线,并调用trimMargin()。
var add = "IJKL"
var text = """
|ABCD
|EFGH
|$add
|MNOP
""".trimMargin()
可选的 (Kě de)
在众多Kotlin功能中,这是其中一个最强大的。
Kotlin被设计为null安全,它的默认设置是不允许出现null值。
举例来说,
val a: String = null
这将会导致编译错误。
因为在Kotlin中默认情况下不允许null,所以会这样。
如果想要允许null,可以在类型后面加上?。
val a: String? = null // これはOK
如果要访问带有可选标记的变量,必须始终进行空值检查。与Java进行比较。
String a = null;
a.contains("hoge"); // 当然nullなのでヌルポで落ちる
var a: String? = null
a.contains("hoge") // そもそもコンパイルが通らない
这段 Kotlin 代码会在编译时被拒绝。
因为它没有保证 NotNull。
要访问 Nullable 的类型,需要这样做。
首先是Java中熟悉的空值检查。
String a = null;
if (a != null) {
a.contains("hoge"); // nullならここは通らない
}
如果使用Java8,可以使用一种被称为Optional的类似功能。
Optional<String> a = Optional.of(null);
a.ifPresent(notNull -> notNull.contains("hoge"))
最重要的Kotlin
val a: String? = null
a?.contains("hoge")
如果是调用可为空类型的函数,只需要像?.那样写即可。
如果a为Null,则不执行contains并且什么也不做。
与Java相比,可以看出Kotlin的编写方式非常简洁。而Java只是在运行时进行检查,即使使用Java8的Optional,也不能解决忘记使用Optional的根本问题。而Kotlin在编译时进行检查,因此不会出现空值检查的遗漏情况。
关于Kotlin的Optional,有足够的内容可以写一篇文章,所以我将它整理到另一篇文章中。
【空安全性】 Kotlin与Java比较备忘录 正确使用空安全的方法。
函数 (方法)
其实我不太明白函数和方法的区别。
在这里,为方便起见,我统一使用函数。
基本句型
アクセス修飾子 戻り値型 関数名(引数型 引数名) {
処理
}
// 例
public int add(int a, int b) {
return a + b;
}
// 呼び出し側
add(2, 3);
アクセス修飾子 fun 関数名(引数名:引数型): 戻り値型 {
処理
}
// 例
fun add(a:Int, b:Int): Int {
return a + b
}
// 呼び出し側
add(2, 3)
在Kotlin中,如果省略了访问修饰符,默认为public。
可变长度参数
public int sum(int... args) {
int sum = 0;
for (int value: args) {
sum++ value;
}
return sum;
}
Kotlin不使用三个句点,而是使用修饰符”vararg”。
fun sum(vararg args:Int) {
var sum:Int = 0
for (value:Int in args) {
sum++ value;
}
return sum
}
具有命名参数的函数
在Kotlin中,调用函数时可以指定参数名称。
例如,在参数较多时特别有效。
public String chaosArguments(String who, String where, String when, String what, String why) {
return when + "に" + where + "で" + who + "が" + what + "を" + why + "だからした";
}
chaosArguments("ウメハラリュウ", "ガイルステージ", "制限時間残り10秒", "昇竜拳", "小足見てから余裕");
// 制限時間残り10秒にガイルステージでウメハラリュウが昇竜拳を小足見てから余裕だからした
太难懂了!!参数顺序也是!例题也是!
fun chaosArguments(who:String, where:String, when:String, what:String, why:String): String {
return "$whenに$whereで$whoが$whatを$whyだからした"
}
chaosArguments(
who = "ウメハラリュウ",
where = "ガイルステージ",
when = "制限時間残り10秒",
what = "昇竜拳",
why = "小足見てから余裕"
)
像这样,
参数名 = 值
可以做到。
可以更清楚地知道把何值传递给哪个参数。
顺便提一下,函数的参数名称不必按照函数参数的定义顺序。
chaosArguments(
where = "ガイルステージ",
when = "制限時間残り10秒",
who = "ウメハラリュウ",
what = "昇竜拳",
why = "小足見てから余裕"
)
可以用其他的方式。
类声明
基本语法
public class ClassName {
// フィールド
private int param1;
private String param2;
// コンストラクタ
public ClassName(int param1, String param2) {
this.param1 = param1;
this.param2 = param2;
}
// メソッド
private void myMethod() {
// 何かの処理
return;
}
}
class ClassName(private val param1: Int, private val param2: String) {
// メソッド
private fun myMethod() {
// 何かの処理
return
}
}
在Kotlin中,与Java不同的是,构造函数是直接在类名之后写的。
据说在Kotlin中被称为主构造函数。
参数部分可以声明修饰符、val和var属性。
顺便提一下,如果主构造函数不带参数,可以省略如下。
class ClassName{
// hogehoge
}
构造函数的各种重载
如果在构造函数内部需要进行某些处理
public class ClassName {
private int param1;
final private String param2;
// コンストラクタ
public ClassName(int param1, String param2) {
this.param1 = param1 + 2;
this.param2 = param2 + "add String";
}
}
class ClassName(param1: Int, param2: String) {
private var param1: Int
private val param2: String
init {
this.param1 = param1 + 2
this.param2 = param2 + "add String"
}
}
如果在构造函数中不直接赋值参数,而是进行某种计算操作的话,可以使用初始化代码块来写入。
在这种情况下,需要像Java一样在顶层声明变量。
如果需要编写多个构造函数的话
public class ClassName {
private int param1;
private String param2;
public ClassName(int param1){
this.param1 = param1;
this.param2 = "initial";
}
public ClassName(int param1, String param2) {
this.param1 = param1;
this.param2 = param2;
}
public ClassName(int param1, String param2, String param3) {
this.param1 = param1;
this.param2 = param2;
String addParam = param3;
}
}
class ClassName {
private var param1: Int
private var param2: String
constructor(param1: Int) {
this.param1 = param1
this.param2 = "initial"
}
constructor(param1: Int, param2: String) {
this.param1 = param1
this.param2 = param2
}
constructor(param1: Int, param2: String, param3: String) {
this.param1 = param1
this.param2 = param2
val addParam = param3
}
}
如果使用构造块,可以添加无数个。
将主构造函数以外的构造函数称为次要构造函数。
如上所述,只需要次要构造函数也可以。
在Java中,可以使用super关键字从次级构造函数调用主构造函数。
public class ClassName {
private int param1;
public ClassName(int param1){
this.param1 = param1;
}
public ClassName(int param1, String param2) {
this(param1);
}
}
class ClassName(private val param1: Int) {
constructor(param1: Int, param2: String) : this(param1) {
}
}
调用构造函数(参数):this(参数)。
设定参数的初始值
例如,可以通过提供多个构造函数来实现以下方式:可以在param1和param2两个参数中都传递参数,或者只传递param1的参数,而param2则使用默认值。这种实现方式在Android中经常看到。在Kotlin中,可以通过非常简单的语句来实现这一点。
public class ClassName {
private int param1;
private String param2;
public ClassName(int param1){
this(param1, "initialize");
}
public ClassName(int param1, String param2) {
this.param1 = param1;
this.param2 = param2;
}
}
class ClassName @JvmOverloads constructor(private val param1: Int, private val param2: String = "initialize")
太厉害了!只需要一行代码就可以描述出来!通过将参数设置为 变量:类型 = 初始值 ,可以指定在没有给定参数的情况下的初始值。
由于Java没有这样的功能,可以使用名为 @JvmOverloads 的注解来解决。
如果你想在Android上创建自定义视图的话
public class CustomView extends View {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
class CustomView : View {
@JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, style: Int = 0) : super(context, attrs, style) {
}
}
用这样超级简单的描述就能成功。太棒了!!
此外,主要构造函数是
类 ClassName(val param1: Int){…} 的汉语本地化表达:
class ClassName(参数1: Int){…}
写代码时通常会用到简写法,例如添加注释或访问修饰符等,则需要正确使用。
类 ClassName public @annotation constructor(val param1: Int){…}
类 ClassName public @annotation constructor(val param1: Int){…} 可以被改写为:
必须写。
类的继承 de
基本的语法结构
// スーパークラス
public class Human {
private String sex;
public Human(String sex) {
this.sex = sex;
}
}
// サブクラス
public class Hero extends Human {
public Hero(String sex) {
super(sex);
}
}
// スーパークラス
open class Human(private var sex: String)
// サブクラス
class Hero(sex: String) : Human(sex)
在类名后面加上冒号并指定超类名。
如果超类有主构造函数,则在继承时必须进行初始化。
开放修饰符
就Java而言,默认情况下可以在没有任何特别操作的情况下继承类,但是Kotlin中没有用open修饰的类无法被继承。
换句话说,Kotlin的类默认情况下相当于Java中的final class。
似乎是要阻止过度便利的继承。
内部类
基本语法结构
public class Hero {
private String name = "まさお";
private void action() {
//インナークラスのフィールドやメソッドはインスタンス化すればアクセスできる。
Equipment eq = new Equipment();
eq.specialAction();
}
// インナークラス
public class Equipment {
private String sword = "木の棒";
private void specialAction() {
// アウタークラスはインスタンス化しなくてもアクセス可能。
String owner = name;
action();
}
}
}
class Hero {
private val name: String? = "まさお"
private fun action() {
// ここはJavaと同じ
val eq = Equipment()
eq.specialAction()
}
inner class Equipment {
private val sword: String = "木の棒"
// アウタークラスからアクセスできるようにするにはアクセス修飾子がinternalかpublicじゃないといけない
internal fun specialAction() {
val owner = name
action()
}
}
}
内部类可以通过以下方式进行定义:
内部类InnerClassName{}
Java和Kotlin有所不同的地方在于,Java中从外部类可以访问内部类的私有方法,但在Kotlin中,如果方法被标记为私有(private),则无法访问。必须使用internal这个访问修饰符。
指定this的实例
如果想要从内部类指定外部类的实例,用Java的写法会有所不同。
public class Hero {
public class Equipment {
Hero hero = Hero.this;
Equipment eq = this;
}
}
class Hero {
inner class Equipment {
val hero = this@Hero
val eq1 = this@Equipment
val eq2= this
}
}
在Java中,可以使用”外部类名.this”来获取外部类的实例。
在Kotlin中,可以使用”this@类名”来指定获取指定类的实例。
在上面的例子中,”this@Equipment”和”this”是相同的。
属性
说到Java中最无聊的代码,那就是getter和setter。
但如果是用Kotlin的话,这个问题就会有炫酷的解决方案。
基本語法结构
public class ClassName {
// これはフィールド
private int param = 0;
// これはアクセサ
public int getParam() {
return param;
}
// これもアクセサ
public void setParam(int mParam) {
param = mParam;
}
}
class Hero {
// これはフィールドではなくプロパティ 内部的にアクセサが生成される
var param = 0
}
只需要一个选项-用中文对以下内容进行释义:
其实,在Kotlin中并不存在字段(field)这个概念,表面上看起来像是字段的东西实际上被称为属性(property)。
在Kotlin中,声明属性时会在内部自动生成访问器(accessor)。
即使不自己创建getter/setter,也可以通过访问器进行引用。
如果使用val进行变量定义,那么显然无法使用setter。
自定义的getter/setter
也可以自己定义getter/setter方法。
public class ClassName {
private int param = 0;
// 絶対値で返す
public int getParam() {
return Math.abs(param);
}
// 絶対値を設定する
public void setParam(int mParam) {
param = Math.abs(mParam);
}
}
class ClassName {
var param = 0
get(){
return Math.abs(param)
}
//set()の引数名は慣例的にvalueにするようだ
set(value) {
field = Math.abs(value)
}
}
在这种情况下,不会内部创建访问器。
在自定义setter中出现的field被称为backing field,它会自动定义用于引用属性值的时候。
For example
var param = 0
//バッキングフィールドを使わない例。循環参照になって落ちる。
set(value) {
param = Math.abs(value)
}
如果创建这样的定制setter,将导致在定制setter中再次调用param的setter,从而导致循环引用。
param = Math.abs(value)
注意,这不是将Math.abs(value)的值赋给param,而是将Math.abs(value)作为param的setter参数传入并调用它的意思。
重写
属性也可以被覆盖。
被覆盖的属性需要像类一样使用open修饰符。
Java的字段将隐藏而不是重写。
参考:http://log.nissuk.info/2012/03/java.html
class SubClass : SuperClass() {
override var param = "sub"
get() = "override"
}
open class SuperClass {
open var param = "super"
}
不仅可以设置值,还可以重写getter/setter。当然,如果从子类中访问param,将返回字符串”override”。
静态字段
在Kotlin中,不存在Static的概念。如果想要处理类似于Java中的Static字段的情况,可以使用”companion object”这个单例对象。
public class ClassName {
public static final int STATIC_VALUE = 1;
public static final
}
class ClassName {
companion object {
@JvmField val STATIC_VALUE = 1
}
}
@JvmField这个注解在从Java端调用时,不需要通过companion object来调用。
如果没有@JvmField,访问Kotlin的STATIC_VALUE的方法是什么?
int value = ClassName.Companion.getSTATIC_VALUE();
然而,对于标有@JvmField注解的属性来说,情况会有所不同。
int value = ClassName.STATIC_VALUE;
可以以与Java的static字段相同的方式访问。
此外,Java的基本类型和字符串类型还有一个叫做const的修饰符。
static final int HOGE = 0;
static final List<String> FUGA = new ArrayList<>();
companion object {
const val HOGE = 0
const val FUGA = ArrayList<String>() // これはダメ。
@JvmField val FUGA = ArrayList<String>() // これはOK。
}
虽然我不太清楚const和@JvmField之间的区别,但根据阅读这篇文章的感觉,它们在原始类型上似乎没有差异。
const和@JvmField之间的差异似乎与编译后的行为有关。
const用于在字段上定义初始值,而@JvmField用于在静态构造函数中定义初始值。
试试看把以下代码转化为字节码来查看。
class A {
companion object {
const val a = 12
@JvmField val b = 12
@JvmField val c = Date() // 非プリミティブ
}
}
// access flags 0x19
public final static I a = 12
// access flags 0x19
public final static I b = 12
@Lkotlin/jvm/JvmField;() // invisible
// access flags 0x19
public final static Ljava/util/Date; c
@Lkotlin/jvm/JvmField;() // invisible
@Lorg/jetbrains/annotations/NotNull;() // invisible
// access flags 0x8
static <clinit>()V
NEW com/hoge/A$Companion
DUP
ACONST_NULL
INVOKESPECIAL com/hoge/A$Companion.<init> (Lkotlin/jvm/internal/DefaultConstructorMarker;)V
PUTSTATIC com/hoge/A.Companion : Lcom/hoge/A$Companion;
L0
LINENUMBER 12 L0
BIPUSH 12
PUTSTATIC com/hoge/A.b : I
L1
LINENUMBER 13 L1
NEW java/util/Date
DUP
INVOKESPECIAL java/util/Date.<init> ()V
PUTSTATIC com/hoge/A.c : Ljava/util/Date;
RETURN
MAXSTACK = 3
MAXLOCALS = 0
总结起来就是这个样子。
初期化のタイミング
const
@JvmField(プリミティブ)
@JvmField(非プリミティブ)
フィールド定義
○
○
×
staticコンストラクタ
×
○
○
如果使用@JvmField来定义原始类型,会导致两次赋予初始值,因此最好将原始类型定义为const,非原始类型定义为@JvmField。
排列
事先仅声明变量,并稍后将实体赋值给它。
String[] a;
a = new String[5];
var a: Array<String?>
a = arrayOfNulls<String>(5)
在Kotlin中,如果在赋值时不明确在类型后面加上?,那么不允许为空值,因此需要使用Array。
可以使用arrayOfNulls<类型>()来创建一个内部为空值的数组。
同时定义变量和实体
String[] a = new String[5];
var a = arrayOfNulls<String>(5)
如果在声明同时将Null赋值,那么?是不需要的。
给予初始值
String[] a = {"a","b","c"};
var a = arrayOf("a","b","c")
使用工厂函数进行初始化。
String[] a = {"0","2","4","6","8"};
var a = Array(5,{ i -> (i * 2).toString() })
在中国,将数组(Array())的第一个参数作为元素数量,第二个参数作为索引值和计算表达式。
操作数组元素
将其应用
a[0]= "test";
a[0] = "test"
a.set(0, "test")
获得
final String element = a[0];
val element = a[0]
val element = a.get(0)
获取要素数量
final int size = a.length;
val size = a.size
获取所有要素
// 添え字付きのループ
for(int i = 0; i < c.length; i++){
final String element = c[i];
}
// 要素を直接取得するループ
for(String elm : c){
final String element = elm;
}
// 添え字付きのループ
for (i in c.indices) {
val element = c[i]
}
// 要素を直接取得するループ
for (elm in c) {
val element = elm
}
// forEachを使った要素を直接取得するループ
c.forEach{elm -> val element = elm}
Kotlin独有的循环方式
// 添え字と要素の中身を同時に取り出す
for ((index, value) in c.withIndex()) {
println("the element at $index is $value.")
}
收藏品
在Kotlin中,除了Java的数组对应的Array之外,当然还有其他与集合相对应的东西。
列出
生成List
List<Integer> list = Arrays.asList(0,1,2);
val list = listOf(0,1,2)
获取要素
final int elm = list.get(0);
val elm = list[0]
val elm = list.get(0)
不变(immutable)和可变(mutable)
如何在List的元素中进行赋值或添加。
包括后面提到的Map和Set在内,Kotlin的集合是不可变的,一旦生成后就无法更改。
而Java的集合是可变的,可以进行赋值和添加操作而无需特意注意。
当然,Kotlin也提供了可变集合,如果想要进行赋值或添加操作,可以使用这个。
增加要素
List<Integer> list = new ArrayList<>();
list.add(0);
val list: MutableList<Int> = mutableListOf()
list.add(0)
替换要素
List<String> list = Arrays.asList("one","two","three");
list.set(0,"new-one");
val list = mutableListOf("one","two","three")
list[0] = "new-one"
删除的要素 de
List<String> list = Arrays.asList("one","two","three");
list.remove(0);
val list = mutableListOf("one","two","three")
list.removeAt(0)
地图
地图基本上与列表相同。
创建地图。
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("one", 1);
put("two", 2);
put("three", 3);
}
};
val map = mapOf("one" to 1, "two" to 2, "three" to 3)
获取值
final int value = map.get("one");
val value = map["one"]
val value = map.get("one")
添加元素 (Tianji yuansu)
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("one", 1);
val map: MutableMap<String, Int> = mutableMapOf()
map.put("one", 1)
元素的替代
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("one", 1);
put("two", 2);
put("three", 3);
}
};
map.put("one", 100);
val map = mutableMapOf("one" to 1, "two" to 2, "three" to 3)
map.put("one", 100)
删除要素
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("one", 1);
put("two", 2);
put("three", 3);
}
};
map.remove("one");
val map = mutableMapOf("one" to 1, "two" to 2, "three" to 3)
map.remove("one")
设定
由于与List、Map几乎相同,故略去。
界面
基礎句構
public interface MyInterface {
// 初期値ありのフィールド
String str = "interface";
// 実装なしのメソッド
// Java8前でも利用可能
void emptyMethod();
// デフォルト実装ありのメソッド
// Java8以降のみ利用可能
void defaultMethod(){
// do something
}
}
interface MyInterface {
// 実装なしのメソッド
fun emptyMethod()
// デフォルト実装ありのメソッド
fun defaultMethod() {
// do something
}
companion object {
val str= "interface"
}
}
抽象属性(字段)
在Java中,无法将字段声明为抽象的,而在Kotlin中可以定义抽象属性。
interface MyInterface {
// 暗黙的にabstractになってる
val abstractStr: String
// getter/setterは定義できる
var strHasAccessor: String
get() = strHasAccessor
set(value) {
strHasAccessor = value
}
// 以下のような実装は不可。初期値を与えることはできない
val str = "init"
}
接口中的属性无法具有初始值。
接口的实现
尝试写一下实现以下接口的类,该接口是用Kotlin编写的。
在Java中,通过extends来表示继承,通过implements来表示实现接口,但在Kotlin中,实现接口的形式为:
class类名 :(冒号)接口名
当然,也可以使用逗号分隔来同时实现多个接口。
interface MyInterface {
// abstractプロパティ
val abstractStr: String
// 実装なしメソッド
fun abstractMethod()
// 実装ありメソッド
fun defaultMethod(){
// do something
}
}
public class MyClass implements MyInterface {
@NonNull
@Override
public String getAbstractStr() {
// return値に設定値を入れる
return "init";
}
@Override
public void abstractMethod() {
// do something
}
// Java8以前ではデフォルト実装ありのメソッドも必ず@Overrideしなくてはいけないようだ
@Override
public void defaultMethod() {
// do something
}
}
class MyClass : MyInterface{
override val abstractStr: String = "init"
override fun abstractMethod() {
}
}
值得注意的是,有以下两个方面:
1. 可以在没有访问器定义的情况下直接为抽象属性设置初始值。
2. 默认实现的defaultMethod()可以由用户决定是否重写。
型推論 –> 类型推论
在 Kotlin 中,由于类型推断的存在,可以省略类型而定义变量。
String str = "文字列";
final int value = 0;
var str = "文字列"
val value = 0
字符序列
字符串的拼接
在Kotlin中,可以在字符串字面值中插入表达式。
String name = "Benjamin Franklin";
int age = 20
String text = "Your name is " + name + ". You'll be " + (age + 1) + " years old next year.";
var name = "Benjamin Franklin"
var age = 20
var text = "Your name is $name. You'll be ${age + 1} years old next year."
如果只插入变量名,则可以省略花括号。
原始字符串字面意思是直接使用字符表示的字符串。
用三个双引号括起来的字符串被称为原始字符串字面量,
它们不会转义特殊字符如反斜杠和换行,并保持原样显示。
String add = "IJKL";
String text = "ABCD\n" +
"EFGH\n" +
add + "\n" +
"MNOP";
var add = "IJKL"
var text = """
ABCD
EFGH
$add
MNOP
"""
如果你想要排除缩进等等的影响,
如果不想要反映缩进,可以在行首放置一个竖线,并调用trimMargin()。
var add = "IJKL"
var text = """
|ABCD
|EFGH
|$add
|MNOP
""".trimMargin()
可选的 (Kě de)
在众多Kotlin功能中,这是其中一个最强大的。
Kotlin被设计为null安全,它的默认设置是不允许出现null值。
举例来说,
val a: String = null
这将会导致编译错误。
因为在Kotlin中默认情况下不允许null,所以会这样。
如果想要允许null,可以在类型后面加上?。
val a: String? = null // これはOK
如果要访问带有可选标记的变量,必须始终进行空值检查。与Java进行比较。
String a = null;
a.contains("hoge"); // 当然nullなのでヌルポで落ちる
var a: String? = null
a.contains("hoge") // そもそもコンパイルが通らない
这段 Kotlin 代码会在编译时被拒绝。
因为它没有保证 NotNull。
要访问 Nullable 的类型,需要这样做。
首先是Java中熟悉的空值检查。
String a = null;
if (a != null) {
a.contains("hoge"); // nullならここは通らない
}
如果使用Java8,可以使用一种被称为Optional的类似功能。
Optional<String> a = Optional.of(null);
a.ifPresent(notNull -> notNull.contains("hoge"))
最重要的Kotlin
val a: String? = null
a?.contains("hoge")
如果是调用可为空类型的函数,只需要像?.那样写即可。
如果a为Null,则不执行contains并且什么也不做。
与Java相比,可以看出Kotlin的编写方式非常简洁。而Java只是在运行时进行检查,即使使用Java8的Optional,也不能解决忘记使用Optional的根本问题。而Kotlin在编译时进行检查,因此不会出现空值检查的遗漏情况。
关于Kotlin的Optional,有足够的内容可以写一篇文章,所以我将它整理到另一篇文章中。
【空安全性】 Kotlin与Java比较备忘录 正确使用空安全的方法。
函数 (方法)
其实我不太明白函数和方法的区别。
在这里,为方便起见,我统一使用函数。
基本句型
アクセス修飾子 戻り値型 関数名(引数型 引数名) {
処理
}
// 例
public int add(int a, int b) {
return a + b;
}
// 呼び出し側
add(2, 3);
アクセス修飾子 fun 関数名(引数名:引数型): 戻り値型 {
処理
}
// 例
fun add(a:Int, b:Int): Int {
return a + b
}
// 呼び出し側
add(2, 3)
アクセス修飾子 戻り値型 関数名(引数型 引数名) {
処理
}
// 例
public int add(int a, int b) {
return a + b;
}
// 呼び出し側
add(2, 3);
アクセス修飾子 fun 関数名(引数名:引数型): 戻り値型 {
処理
}
// 例
fun add(a:Int, b:Int): Int {
return a + b
}
// 呼び出し側
add(2, 3)
在Kotlin中,如果省略了访问修饰符,默认为public。
可变长度参数
public int sum(int... args) {
int sum = 0;
for (int value: args) {
sum++ value;
}
return sum;
}
public int sum(int... args) {
int sum = 0;
for (int value: args) {
sum++ value;
}
return sum;
}
Kotlin不使用三个句点,而是使用修饰符”vararg”。
fun sum(vararg args:Int) {
var sum:Int = 0
for (value:Int in args) {
sum++ value;
}
return sum
}
具有命名参数的函数
在Kotlin中,调用函数时可以指定参数名称。
例如,在参数较多时特别有效。
public String chaosArguments(String who, String where, String when, String what, String why) {
return when + "に" + where + "で" + who + "が" + what + "を" + why + "だからした";
}
chaosArguments("ウメハラリュウ", "ガイルステージ", "制限時間残り10秒", "昇竜拳", "小足見てから余裕");
// 制限時間残り10秒にガイルステージでウメハラリュウが昇竜拳を小足見てから余裕だからした
太难懂了!!参数顺序也是!例题也是!
fun chaosArguments(who:String, where:String, when:String, what:String, why:String): String {
return "$whenに$whereで$whoが$whatを$whyだからした"
}
chaosArguments(
who = "ウメハラリュウ",
where = "ガイルステージ",
when = "制限時間残り10秒",
what = "昇竜拳",
why = "小足見てから余裕"
)
像这样,
参数名 = 值
可以做到。
可以更清楚地知道把何值传递给哪个参数。
顺便提一下,函数的参数名称不必按照函数参数的定义顺序。
chaosArguments(
where = "ガイルステージ",
when = "制限時間残り10秒",
who = "ウメハラリュウ",
what = "昇竜拳",
why = "小足見てから余裕"
)
可以用其他的方式。
类声明
基本语法
public class ClassName {
// フィールド
private int param1;
private String param2;
// コンストラクタ
public ClassName(int param1, String param2) {
this.param1 = param1;
this.param2 = param2;
}
// メソッド
private void myMethod() {
// 何かの処理
return;
}
}
class ClassName(private val param1: Int, private val param2: String) {
// メソッド
private fun myMethod() {
// 何かの処理
return
}
}
public class ClassName {
// フィールド
private int param1;
private String param2;
// コンストラクタ
public ClassName(int param1, String param2) {
this.param1 = param1;
this.param2 = param2;
}
// メソッド
private void myMethod() {
// 何かの処理
return;
}
}
class ClassName(private val param1: Int, private val param2: String) {
// メソッド
private fun myMethod() {
// 何かの処理
return
}
}
在Kotlin中,与Java不同的是,构造函数是直接在类名之后写的。
据说在Kotlin中被称为主构造函数。
参数部分可以声明修饰符、val和var属性。
顺便提一下,如果主构造函数不带参数,可以省略如下。
class ClassName{
// hogehoge
}
构造函数的各种重载
如果在构造函数内部需要进行某些处理
public class ClassName {
private int param1;
final private String param2;
// コンストラクタ
public ClassName(int param1, String param2) {
this.param1 = param1 + 2;
this.param2 = param2 + "add String";
}
}
class ClassName(param1: Int, param2: String) {
private var param1: Int
private val param2: String
init {
this.param1 = param1 + 2
this.param2 = param2 + "add String"
}
}
public class ClassName {
private int param1;
final private String param2;
// コンストラクタ
public ClassName(int param1, String param2) {
this.param1 = param1 + 2;
this.param2 = param2 + "add String";
}
}
class ClassName(param1: Int, param2: String) {
private var param1: Int
private val param2: String
init {
this.param1 = param1 + 2
this.param2 = param2 + "add String"
}
}
如果在构造函数中不直接赋值参数,而是进行某种计算操作的话,可以使用初始化代码块来写入。
在这种情况下,需要像Java一样在顶层声明变量。
如果需要编写多个构造函数的话
public class ClassName {
private int param1;
private String param2;
public ClassName(int param1){
this.param1 = param1;
this.param2 = "initial";
}
public ClassName(int param1, String param2) {
this.param1 = param1;
this.param2 = param2;
}
public ClassName(int param1, String param2, String param3) {
this.param1 = param1;
this.param2 = param2;
String addParam = param3;
}
}
class ClassName {
private var param1: Int
private var param2: String
constructor(param1: Int) {
this.param1 = param1
this.param2 = "initial"
}
constructor(param1: Int, param2: String) {
this.param1 = param1
this.param2 = param2
}
constructor(param1: Int, param2: String, param3: String) {
this.param1 = param1
this.param2 = param2
val addParam = param3
}
}
public class ClassName {
private int param1;
private String param2;
public ClassName(int param1){
this.param1 = param1;
this.param2 = "initial";
}
public ClassName(int param1, String param2) {
this.param1 = param1;
this.param2 = param2;
}
public ClassName(int param1, String param2, String param3) {
this.param1 = param1;
this.param2 = param2;
String addParam = param3;
}
}
class ClassName {
private var param1: Int
private var param2: String
constructor(param1: Int) {
this.param1 = param1
this.param2 = "initial"
}
constructor(param1: Int, param2: String) {
this.param1 = param1
this.param2 = param2
}
constructor(param1: Int, param2: String, param3: String) {
this.param1 = param1
this.param2 = param2
val addParam = param3
}
}
如果使用构造块,可以添加无数个。
将主构造函数以外的构造函数称为次要构造函数。
如上所述,只需要次要构造函数也可以。
在Java中,可以使用super关键字从次级构造函数调用主构造函数。
public class ClassName {
private int param1;
public ClassName(int param1){
this.param1 = param1;
}
public ClassName(int param1, String param2) {
this(param1);
}
}
class ClassName(private val param1: Int) {
constructor(param1: Int, param2: String) : this(param1) {
}
}
public class ClassName {
private int param1;
public ClassName(int param1){
this.param1 = param1;
}
public ClassName(int param1, String param2) {
this(param1);
}
}
class ClassName(private val param1: Int) {
constructor(param1: Int, param2: String) : this(param1) {
}
}
调用构造函数(参数):this(参数)。
设定参数的初始值
例如,可以通过提供多个构造函数来实现以下方式:可以在param1和param2两个参数中都传递参数,或者只传递param1的参数,而param2则使用默认值。这种实现方式在Android中经常看到。在Kotlin中,可以通过非常简单的语句来实现这一点。
public class ClassName {
private int param1;
private String param2;
public ClassName(int param1){
this(param1, "initialize");
}
public ClassName(int param1, String param2) {
this.param1 = param1;
this.param2 = param2;
}
}
class ClassName @JvmOverloads constructor(private val param1: Int, private val param2: String = "initialize")
太厉害了!只需要一行代码就可以描述出来!通过将参数设置为 变量:类型 = 初始值 ,可以指定在没有给定参数的情况下的初始值。
由于Java没有这样的功能,可以使用名为 @JvmOverloads 的注解来解决。
如果你想在Android上创建自定义视图的话
public class CustomView extends View {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
class CustomView : View {
@JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, style: Int = 0) : super(context, attrs, style) {
}
}
用这样超级简单的描述就能成功。太棒了!!
此外,主要构造函数是
类 ClassName(val param1: Int){…} 的汉语本地化表达:
class ClassName(参数1: Int){…}
写代码时通常会用到简写法,例如添加注释或访问修饰符等,则需要正确使用。
类 ClassName public @annotation constructor(val param1: Int){…}
类 ClassName public @annotation constructor(val param1: Int){…} 可以被改写为:
必须写。
类的继承 de
基本的语法结构
// スーパークラス
public class Human {
private String sex;
public Human(String sex) {
this.sex = sex;
}
}
// サブクラス
public class Hero extends Human {
public Hero(String sex) {
super(sex);
}
}
// スーパークラス
open class Human(private var sex: String)
// サブクラス
class Hero(sex: String) : Human(sex)
// スーパークラス
public class Human {
private String sex;
public Human(String sex) {
this.sex = sex;
}
}
// サブクラス
public class Hero extends Human {
public Hero(String sex) {
super(sex);
}
}
// スーパークラス
open class Human(private var sex: String)
// サブクラス
class Hero(sex: String) : Human(sex)
在类名后面加上冒号并指定超类名。
如果超类有主构造函数,则在继承时必须进行初始化。
开放修饰符
就Java而言,默认情况下可以在没有任何特别操作的情况下继承类,但是Kotlin中没有用open修饰的类无法被继承。
换句话说,Kotlin的类默认情况下相当于Java中的final class。
似乎是要阻止过度便利的继承。
内部类
基本语法结构
public class Hero {
private String name = "まさお";
private void action() {
//インナークラスのフィールドやメソッドはインスタンス化すればアクセスできる。
Equipment eq = new Equipment();
eq.specialAction();
}
// インナークラス
public class Equipment {
private String sword = "木の棒";
private void specialAction() {
// アウタークラスはインスタンス化しなくてもアクセス可能。
String owner = name;
action();
}
}
}
class Hero {
private val name: String? = "まさお"
private fun action() {
// ここはJavaと同じ
val eq = Equipment()
eq.specialAction()
}
inner class Equipment {
private val sword: String = "木の棒"
// アウタークラスからアクセスできるようにするにはアクセス修飾子がinternalかpublicじゃないといけない
internal fun specialAction() {
val owner = name
action()
}
}
}
public class Hero {
private String name = "まさお";
private void action() {
//インナークラスのフィールドやメソッドはインスタンス化すればアクセスできる。
Equipment eq = new Equipment();
eq.specialAction();
}
// インナークラス
public class Equipment {
private String sword = "木の棒";
private void specialAction() {
// アウタークラスはインスタンス化しなくてもアクセス可能。
String owner = name;
action();
}
}
}
class Hero {
private val name: String? = "まさお"
private fun action() {
// ここはJavaと同じ
val eq = Equipment()
eq.specialAction()
}
inner class Equipment {
private val sword: String = "木の棒"
// アウタークラスからアクセスできるようにするにはアクセス修飾子がinternalかpublicじゃないといけない
internal fun specialAction() {
val owner = name
action()
}
}
}
内部类可以通过以下方式进行定义:
内部类InnerClassName{}
Java和Kotlin有所不同的地方在于,Java中从外部类可以访问内部类的私有方法,但在Kotlin中,如果方法被标记为私有(private),则无法访问。必须使用internal这个访问修饰符。
指定this的实例
如果想要从内部类指定外部类的实例,用Java的写法会有所不同。
public class Hero {
public class Equipment {
Hero hero = Hero.this;
Equipment eq = this;
}
}
class Hero {
inner class Equipment {
val hero = this@Hero
val eq1 = this@Equipment
val eq2= this
}
}
在Java中,可以使用”外部类名.this”来获取外部类的实例。
在Kotlin中,可以使用”this@类名”来指定获取指定类的实例。
在上面的例子中,”this@Equipment”和”this”是相同的。
属性
说到Java中最无聊的代码,那就是getter和setter。
但如果是用Kotlin的话,这个问题就会有炫酷的解决方案。
基本語法结构
public class ClassName {
// これはフィールド
private int param = 0;
// これはアクセサ
public int getParam() {
return param;
}
// これもアクセサ
public void setParam(int mParam) {
param = mParam;
}
}
class Hero {
// これはフィールドではなくプロパティ 内部的にアクセサが生成される
var param = 0
}
public class ClassName {
// これはフィールド
private int param = 0;
// これはアクセサ
public int getParam() {
return param;
}
// これもアクセサ
public void setParam(int mParam) {
param = mParam;
}
}
class Hero {
// これはフィールドではなくプロパティ 内部的にアクセサが生成される
var param = 0
}
只需要一个选项-用中文对以下内容进行释义:
其实,在Kotlin中并不存在字段(field)这个概念,表面上看起来像是字段的东西实际上被称为属性(property)。
在Kotlin中,声明属性时会在内部自动生成访问器(accessor)。
即使不自己创建getter/setter,也可以通过访问器进行引用。
如果使用val进行变量定义,那么显然无法使用setter。
自定义的getter/setter
也可以自己定义getter/setter方法。
public class ClassName {
private int param = 0;
// 絶対値で返す
public int getParam() {
return Math.abs(param);
}
// 絶対値を設定する
public void setParam(int mParam) {
param = Math.abs(mParam);
}
}
class ClassName {
var param = 0
get(){
return Math.abs(param)
}
//set()の引数名は慣例的にvalueにするようだ
set(value) {
field = Math.abs(value)
}
}
在这种情况下,不会内部创建访问器。
在自定义setter中出现的field被称为backing field,它会自动定义用于引用属性值的时候。
For example
var param = 0
//バッキングフィールドを使わない例。循環参照になって落ちる。
set(value) {
param = Math.abs(value)
}
如果创建这样的定制setter,将导致在定制setter中再次调用param的setter,从而导致循环引用。
param = Math.abs(value)
注意,这不是将Math.abs(value)的值赋给param,而是将Math.abs(value)作为param的setter参数传入并调用它的意思。
重写
属性也可以被覆盖。
被覆盖的属性需要像类一样使用open修饰符。
Java的字段将隐藏而不是重写。
参考:http://log.nissuk.info/2012/03/java.html
class SubClass : SuperClass() {
override var param = "sub"
get() = "override"
}
open class SuperClass {
open var param = "super"
}
不仅可以设置值,还可以重写getter/setter。当然,如果从子类中访问param,将返回字符串”override”。
静态字段
在Kotlin中,不存在Static的概念。如果想要处理类似于Java中的Static字段的情况,可以使用”companion object”这个单例对象。
public class ClassName {
public static final int STATIC_VALUE = 1;
public static final
}
class ClassName {
companion object {
@JvmField val STATIC_VALUE = 1
}
}
@JvmField这个注解在从Java端调用时,不需要通过companion object来调用。
如果没有@JvmField,访问Kotlin的STATIC_VALUE的方法是什么?
int value = ClassName.Companion.getSTATIC_VALUE();
然而,对于标有@JvmField注解的属性来说,情况会有所不同。
int value = ClassName.STATIC_VALUE;
可以以与Java的static字段相同的方式访问。
此外,Java的基本类型和字符串类型还有一个叫做const的修饰符。
static final int HOGE = 0;
static final List<String> FUGA = new ArrayList<>();
companion object {
const val HOGE = 0
const val FUGA = ArrayList<String>() // これはダメ。
@JvmField val FUGA = ArrayList<String>() // これはOK。
}
虽然我不太清楚const和@JvmField之间的区别,但根据阅读这篇文章的感觉,它们在原始类型上似乎没有差异。
const和@JvmField之间的差异似乎与编译后的行为有关。
const用于在字段上定义初始值,而@JvmField用于在静态构造函数中定义初始值。
试试看把以下代码转化为字节码来查看。
class A {
companion object {
const val a = 12
@JvmField val b = 12
@JvmField val c = Date() // 非プリミティブ
}
}
// access flags 0x19
public final static I a = 12
// access flags 0x19
public final static I b = 12
@Lkotlin/jvm/JvmField;() // invisible
// access flags 0x19
public final static Ljava/util/Date; c
@Lkotlin/jvm/JvmField;() // invisible
@Lorg/jetbrains/annotations/NotNull;() // invisible
// access flags 0x8
static <clinit>()V
NEW com/hoge/A$Companion
DUP
ACONST_NULL
INVOKESPECIAL com/hoge/A$Companion.<init> (Lkotlin/jvm/internal/DefaultConstructorMarker;)V
PUTSTATIC com/hoge/A.Companion : Lcom/hoge/A$Companion;
L0
LINENUMBER 12 L0
BIPUSH 12
PUTSTATIC com/hoge/A.b : I
L1
LINENUMBER 13 L1
NEW java/util/Date
DUP
INVOKESPECIAL java/util/Date.<init> ()V
PUTSTATIC com/hoge/A.c : Ljava/util/Date;
RETURN
MAXSTACK = 3
MAXLOCALS = 0
总结起来就是这个样子。
如果使用@JvmField来定义原始类型,会导致两次赋予初始值,因此最好将原始类型定义为const,非原始类型定义为@JvmField。
排列
事先仅声明变量,并稍后将实体赋值给它。
String[] a;
a = new String[5];
var a: Array<String?>
a = arrayOfNulls<String>(5)
String[] a;
a = new String[5];
var a: Array<String?>
a = arrayOfNulls<String>(5)
在Kotlin中,如果在赋值时不明确在类型后面加上?,那么不允许为空值,因此需要使用Array
可以使用arrayOfNulls<类型>()来创建一个内部为空值的数组。
同时定义变量和实体
String[] a = new String[5];
var a = arrayOfNulls<String>(5)
String[] a = new String[5];
var a = arrayOfNulls<String>(5)
如果在声明同时将Null赋值,那么?是不需要的。
给予初始值
String[] a = {"a","b","c"};
var a = arrayOf("a","b","c")
使用工厂函数进行初始化。
String[] a = {"0","2","4","6","8"};
var a = Array(5,{ i -> (i * 2).toString() })
String[] a = {"a","b","c"};
var a = arrayOf("a","b","c")
String[] a = {"0","2","4","6","8"};
var a = Array(5,{ i -> (i * 2).toString() })
在中国,将数组(Array())的第一个参数作为元素数量,第二个参数作为索引值和计算表达式。
操作数组元素
将其应用
a[0]= "test";
a[0] = "test"
a.set(0, "test")
获得
final String element = a[0];
val element = a[0]
val element = a.get(0)
获取要素数量
final int size = a.length;
val size = a.size
获取所有要素
// 添え字付きのループ
for(int i = 0; i < c.length; i++){
final String element = c[i];
}
// 要素を直接取得するループ
for(String elm : c){
final String element = elm;
}
// 添え字付きのループ
for (i in c.indices) {
val element = c[i]
}
// 要素を直接取得するループ
for (elm in c) {
val element = elm
}
// forEachを使った要素を直接取得するループ
c.forEach{elm -> val element = elm}
Kotlin独有的循环方式
// 添え字と要素の中身を同時に取り出す
for ((index, value) in c.withIndex()) {
println("the element at $index is $value.")
}
收藏品
a[0]= "test";
a[0] = "test"
a.set(0, "test")
获得
final String element = a[0];
val element = a[0]
val element = a.get(0)
获取要素数量
final int size = a.length;
val size = a.size
获取所有要素
// 添え字付きのループ
for(int i = 0; i < c.length; i++){
final String element = c[i];
}
// 要素を直接取得するループ
for(String elm : c){
final String element = elm;
}
// 添え字付きのループ
for (i in c.indices) {
val element = c[i]
}
// 要素を直接取得するループ
for (elm in c) {
val element = elm
}
// forEachを使った要素を直接取得するループ
c.forEach{elm -> val element = elm}
Kotlin独有的循环方式
// 添え字と要素の中身を同時に取り出す
for ((index, value) in c.withIndex()) {
println("the element at $index is $value.")
}
收藏品
final String element = a[0];
val element = a[0]
val element = a.get(0)
final int size = a.length;
val size = a.size
获取所有要素
// 添え字付きのループ
for(int i = 0; i < c.length; i++){
final String element = c[i];
}
// 要素を直接取得するループ
for(String elm : c){
final String element = elm;
}
// 添え字付きのループ
for (i in c.indices) {
val element = c[i]
}
// 要素を直接取得するループ
for (elm in c) {
val element = elm
}
// forEachを使った要素を直接取得するループ
c.forEach{elm -> val element = elm}
Kotlin独有的循环方式
// 添え字と要素の中身を同時に取り出す
for ((index, value) in c.withIndex()) {
println("the element at $index is $value.")
}
收藏品
// 添え字付きのループ
for(int i = 0; i < c.length; i++){
final String element = c[i];
}
// 要素を直接取得するループ
for(String elm : c){
final String element = elm;
}
// 添え字付きのループ
for (i in c.indices) {
val element = c[i]
}
// 要素を直接取得するループ
for (elm in c) {
val element = elm
}
// forEachを使った要素を直接取得するループ
c.forEach{elm -> val element = elm}
// 添え字と要素の中身を同時に取り出す
for ((index, value) in c.withIndex()) {
println("the element at $index is $value.")
}
收藏品
在Kotlin中,除了Java的数组对应的Array之外,当然还有其他与集合相对应的东西。
列出
生成List
List<Integer> list = Arrays.asList(0,1,2);
val list = listOf(0,1,2)
获取要素
final int elm = list.get(0);
val elm = list[0]
val elm = list.get(0)
不变(immutable)和可变(mutable)
List<Integer> list = Arrays.asList(0,1,2);
val list = listOf(0,1,2)
获取要素
final int elm = list.get(0);
val elm = list[0]
val elm = list.get(0)
不变(immutable)和可变(mutable)
final int elm = list.get(0);
val elm = list[0]
val elm = list.get(0)
如何在List的元素中进行赋值或添加。
包括后面提到的Map和Set在内,Kotlin的集合是不可变的,一旦生成后就无法更改。
而Java的集合是可变的,可以进行赋值和添加操作而无需特意注意。
当然,Kotlin也提供了可变集合,如果想要进行赋值或添加操作,可以使用这个。
增加要素
List<Integer> list = new ArrayList<>();
list.add(0);
val list: MutableList<Int> = mutableListOf()
list.add(0)
替换要素
List<String> list = Arrays.asList("one","two","three");
list.set(0,"new-one");
val list = mutableListOf("one","two","three")
list[0] = "new-one"
删除的要素 de
List<String> list = Arrays.asList("one","two","three");
list.remove(0);
val list = mutableListOf("one","two","three")
list.removeAt(0)
地图
List<Integer> list = new ArrayList<>();
list.add(0);
val list: MutableList<Int> = mutableListOf()
list.add(0)
List<String> list = Arrays.asList("one","two","three");
list.set(0,"new-one");
val list = mutableListOf("one","two","three")
list[0] = "new-one"
删除的要素 de
List<String> list = Arrays.asList("one","two","three");
list.remove(0);
val list = mutableListOf("one","two","three")
list.removeAt(0)
地图
List<String> list = Arrays.asList("one","two","three");
list.remove(0);
val list = mutableListOf("one","two","three")
list.removeAt(0)
地图基本上与列表相同。
创建地图。
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("one", 1);
put("two", 2);
put("three", 3);
}
};
val map = mapOf("one" to 1, "two" to 2, "three" to 3)
获取值
final int value = map.get("one");
val value = map["one"]
val value = map.get("one")
添加元素 (Tianji yuansu)
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("one", 1);
val map: MutableMap<String, Int> = mutableMapOf()
map.put("one", 1)
元素的替代
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("one", 1);
put("two", 2);
put("three", 3);
}
};
map.put("one", 100);
val map = mutableMapOf("one" to 1, "two" to 2, "three" to 3)
map.put("one", 100)
删除要素
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("one", 1);
put("two", 2);
put("three", 3);
}
};
map.remove("one");
val map = mutableMapOf("one" to 1, "two" to 2, "three" to 3)
map.remove("one")
设定
由于与List、Map几乎相同,故略去。
界面
基礎句構
public interface MyInterface {
// 初期値ありのフィールド
String str = "interface";
// 実装なしのメソッド
// Java8前でも利用可能
void emptyMethod();
// デフォルト実装ありのメソッド
// Java8以降のみ利用可能
void defaultMethod(){
// do something
}
}
interface MyInterface {
// 実装なしのメソッド
fun emptyMethod()
// デフォルト実装ありのメソッド
fun defaultMethod() {
// do something
}
companion object {
val str= "interface"
}
}
抽象属性(字段)
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("one", 1);
put("two", 2);
put("three", 3);
}
};
val map = mapOf("one" to 1, "two" to 2, "three" to 3)
final int value = map.get("one");
val value = map["one"]
val value = map.get("one")
添加元素 (Tianji yuansu)
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("one", 1);
val map: MutableMap<String, Int> = mutableMapOf()
map.put("one", 1)
元素的替代
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("one", 1);
put("two", 2);
put("three", 3);
}
};
map.put("one", 100);
val map = mutableMapOf("one" to 1, "two" to 2, "three" to 3)
map.put("one", 100)
删除要素
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("one", 1);
put("two", 2);
put("three", 3);
}
};
map.remove("one");
val map = mutableMapOf("one" to 1, "two" to 2, "three" to 3)
map.remove("one")
设定
由于与List、Map几乎相同,故略去。
界面
基礎句構
public interface MyInterface {
// 初期値ありのフィールド
String str = "interface";
// 実装なしのメソッド
// Java8前でも利用可能
void emptyMethod();
// デフォルト実装ありのメソッド
// Java8以降のみ利用可能
void defaultMethod(){
// do something
}
}
interface MyInterface {
// 実装なしのメソッド
fun emptyMethod()
// デフォルト実装ありのメソッド
fun defaultMethod() {
// do something
}
companion object {
val str= "interface"
}
}
抽象属性(字段)
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("one", 1);
val map: MutableMap<String, Int> = mutableMapOf()
map.put("one", 1)
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("one", 1);
put("two", 2);
put("three", 3);
}
};
map.put("one", 100);
val map = mutableMapOf("one" to 1, "two" to 2, "three" to 3)
map.put("one", 100)
删除要素
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("one", 1);
put("two", 2);
put("three", 3);
}
};
map.remove("one");
val map = mutableMapOf("one" to 1, "two" to 2, "three" to 3)
map.remove("one")
设定
由于与List、Map几乎相同,故略去。
界面
基礎句構
public interface MyInterface {
// 初期値ありのフィールド
String str = "interface";
// 実装なしのメソッド
// Java8前でも利用可能
void emptyMethod();
// デフォルト実装ありのメソッド
// Java8以降のみ利用可能
void defaultMethod(){
// do something
}
}
interface MyInterface {
// 実装なしのメソッド
fun emptyMethod()
// デフォルト実装ありのメソッド
fun defaultMethod() {
// do something
}
companion object {
val str= "interface"
}
}
抽象属性(字段)
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("one", 1);
put("two", 2);
put("three", 3);
}
};
map.remove("one");
val map = mutableMapOf("one" to 1, "two" to 2, "three" to 3)
map.remove("one")
由于与List、Map几乎相同,故略去。
界面
基礎句構
public interface MyInterface {
// 初期値ありのフィールド
String str = "interface";
// 実装なしのメソッド
// Java8前でも利用可能
void emptyMethod();
// デフォルト実装ありのメソッド
// Java8以降のみ利用可能
void defaultMethod(){
// do something
}
}
interface MyInterface {
// 実装なしのメソッド
fun emptyMethod()
// デフォルト実装ありのメソッド
fun defaultMethod() {
// do something
}
companion object {
val str= "interface"
}
}
抽象属性(字段)
基礎句構
public interface MyInterface {
// 初期値ありのフィールド
String str = "interface";
// 実装なしのメソッド
// Java8前でも利用可能
void emptyMethod();
// デフォルト実装ありのメソッド
// Java8以降のみ利用可能
void defaultMethod(){
// do something
}
}
interface MyInterface {
// 実装なしのメソッド
fun emptyMethod()
// デフォルト実装ありのメソッド
fun defaultMethod() {
// do something
}
companion object {
val str= "interface"
}
}
抽象属性(字段)
public interface MyInterface {
// 初期値ありのフィールド
String str = "interface";
// 実装なしのメソッド
// Java8前でも利用可能
void emptyMethod();
// デフォルト実装ありのメソッド
// Java8以降のみ利用可能
void defaultMethod(){
// do something
}
}
interface MyInterface {
// 実装なしのメソッド
fun emptyMethod()
// デフォルト実装ありのメソッド
fun defaultMethod() {
// do something
}
companion object {
val str= "interface"
}
}
在Java中,无法将字段声明为抽象的,而在Kotlin中可以定义抽象属性。
interface MyInterface {
// 暗黙的にabstractになってる
val abstractStr: String
// getter/setterは定義できる
var strHasAccessor: String
get() = strHasAccessor
set(value) {
strHasAccessor = value
}
// 以下のような実装は不可。初期値を与えることはできない
val str = "init"
}
接口中的属性无法具有初始值。
接口的实现
尝试写一下实现以下接口的类,该接口是用Kotlin编写的。
在Java中,通过extends来表示继承,通过implements来表示实现接口,但在Kotlin中,实现接口的形式为:
class类名 :(冒号)接口名
当然,也可以使用逗号分隔来同时实现多个接口。
interface MyInterface {
// abstractプロパティ
val abstractStr: String
// 実装なしメソッド
fun abstractMethod()
// 実装ありメソッド
fun defaultMethod(){
// do something
}
}
public class MyClass implements MyInterface {
@NonNull
@Override
public String getAbstractStr() {
// return値に設定値を入れる
return "init";
}
@Override
public void abstractMethod() {
// do something
}
// Java8以前ではデフォルト実装ありのメソッドも必ず@Overrideしなくてはいけないようだ
@Override
public void defaultMethod() {
// do something
}
}
class MyClass : MyInterface{
override val abstractStr: String = "init"
override fun abstractMethod() {
}
}
值得注意的是,有以下两个方面:
1. 可以在没有访问器定义的情况下直接为抽象属性设置初始值。
2. 默认实现的defaultMethod()可以由用户决定是否重写。