kotlin的一些语法关键词收集,包括一些网上的资料以及自己遇到的一些kotlin的问题解决
var与val
var表示java中的变量申明,而val表示常量的申明
Kotlin不允许声明变量但不初始化
赋非空值
var str: String = ""
设为null
var str: String? = null
强制设为null
var str: String = null!!
Kotlin是空安全的,但是第三种是例外情况,如果给一个变量赋值为null!!,那么就等于声明这个变量不是空安全的,就算这样使用
var str:String = null!!
str.length
lateinit:延迟初始化属性
都知道的是,在类内声明的属性必须初始化,如果设置非NULL的属性,应该将此属性在构造器内进行初始化。假如想在类内声明一个NULL属性,在需要时再进行初始化,与Kotlin的规则是相背的,此时我们可以声明一个属性并延迟其初始化,此属性用lateinit修饰符修饰。
XX:Bundle
?
代表xx允许为空
?:操作符
如果 ?: 左侧的表达式值不是null, 就会返回表达式的的值,否则, 返回右侧表达式的值.
用 ?. 运算符来访问一个可空的变量。
用 ?: 运算符来指定当该变量为空时的替代值
open class 的使用
使用open该关键字表示当前类需要被重写,与java中的final关键字相反,如果不希望该类被重写,可以使用final关键字
Unit
只有一个值的类型:单元对象。这种类型对应于Java中的“void”类型。
(pos : Int) ->
Unit
他的输入为
int
,返回值为
Unit
as
as相当于java中的强制类型转换
"""
原始字符串(raw string)由三重引号(“”“)分隔。
原始字符串可以包含换行符和任何其他字符。
val fooRawString = """
vararg
fun varargExample(vararg names: Int)
用“vararg”关键字标记函数参数,
则允许将可变数量的参数传递给函数。
单个表达式
fun odd(x: Int): Boolean = x % 2 == 1
当函数由单个表达式组成时,可以省略大括号,
直接在 = 符号后写函数体。
::
用 :: 操作符把一个命名函数用作参数
it
如果lambda只有一个参数,
那么它的声明可以(与 -> 一起)省略。
单个参数的名称将是“it”。
val notPositive = not {it > 0}
调用构造函数来创建一个实例。
注意,Kotlin没有 “new” 这个关键字。
val fooExampleClass = ExampleClass(7)
infix
如果一个函数用 “infix” 关键字标记,称为中缀标记。
println(fooExampleClass infixMemberFunction 4) // => 28
dataclass
在kotlin中 class前添加data可以实现javabean的效果,“hashCode”、“equals”和“toString”方法都是自动生成的。
with
"with" 方法与JavaScript中的 "with" 声明类似.是kotlin中一种代替swich的方式,
"when" 可以带参数,"when" 也可以当作函数,并返回值.
Of的使用
listOf
可以用 "listOf" 方法创建一个list。 这个list是不可变的 —— 不能增加或删除元素。
可以通过序号获取list中的元素。
println(fooList[1]) // => b
setOf
用 "setOf" 方法创建集合.
val fooSet = setOf("a", "b", "c")
mapOf
用 "mapOf" 方法创建 map.
val fooMap = mapOf("a" to 8, "b" to 7, "c" to 9)
在map中,通过 kep 来获取 value.
println(fooMap["a"]) // => 8
if
“if” 表达式可以,
"when" 可以带参数.
直接返回值。
因此,Kotlin 不需要三元运算符 ?:
is
可以用“is”运算符检查对象是否是特定类型。
如果对象通过了类型检查,就可以把它当作这个类型来用,无需显示转换。
if (x is Boolean) {
// x is automatically cast to Boolean
return x
}
扩展方法
在kotlin中允许开发者在已写好的接口类中添加扩展方法,方法名与要扩展的方法名字一致就可添加
enum枚举类
枚举类与Java的枚举类型相似.
enum class EnumExample {
A, B, C
}
object
"object" 关键字可用于创建单例对象。
我们不能实例化它,但我们可以通过它的名称来引用它的唯一实例。
这与Scala的单例对象类似。
*/
object ObjectExample {
fun hello(): String {
return "hello"
}
}
fun useObject() {
ObjectExample.hello()
val someRef: Any = ObjectExample // 可以直接使用对象名称
}
const
如果属性值载编译期间就能确定,则可以使用
const
修饰符,将属性标记为编译器常数值,这类属性必须满足以下所有条件:
- 必须是顶级属性,或者是一个object的成员
- 值被初始化为String类型,或基本类型
- 不存自定义的取值方法
其效果相当于java中的static,但是其内容必须确定是一个常量的值
!!与?
空判断
Kotlin的空安全设计对于声明可为空的参数,在使用时要进行空判断处理,有两种处理方式一种像Java一样抛出空异常,字段后加 !! ,另一种不做处理直接跳过,字段后加 ?
$字符串模板
在Java中拼接字符串的代码可读性都很差,在Kotlin字符串拼接变得非常简洁,只需用 $ 后面加上参数名,复杂的参数要加上 {}
val user = User()
//赋值
user.name = "tutu"
user.age = "23"
//取值
val name = user.name
val age = user.age
var userInfo = "name:${user.name}, age:$age"
//输出结果:name:tutu, age:23
companion object
伴生对象(静态变量)
Kotlin一般在类中不允许static成员,可以使用companion object来创建一个伴生对象从而可以使用static成员.
高阶函数(Higher-Order Functions )
Kotlin还有一个Java不具备的特性,就是给方法传参数的时候,这个参数也可以是个方法。多么神奇。我们写个方法,就是从一个List种取出偶数,组成一个新的List。
我们用Java写:
public List<Integer> filter(List<Integer> list) {
List<Integer> resultList = new ArrayList<>();
for (int t : list) {
if (t % 2 == 0) {
resultList.add(t);
}
}
return resultList;
}
我们用Kotlin写:
fun filter(items: Collection<Int>, f: (Int) -> Boolean): List<Int> {
val filtered = arrayListOf<Int>()
for (item in filtered) {
if (f(item)) {
filtered.add(item)
}
}
return filtered
}
在Kotlin中,方法的参数类型是在:的后面。(Int) -> Boolean这个是上面方法的第二个参数的参数类型。表示这个参数是个方法并且这个方法的传入的是一个Int类型的值,返回值是Boolean类型的值。那么怎么调用这个上面这个方法呢?
val numbers = listOf<Int>(1, 2, 3, 4)
// 得到所有偶数组成的List
filter(numbers, {
it % 2 == 0
})
在Kotlin中当一个方法的方法参数是最后一个的时候我们还可以写成这样
val numbers = listOf<Int>(1, 2, 3, 4)
// 可以把大挂号放外面
filter(numbers) {
it % 2 == 0
}
如果我们想得到List中的奇数组成的List可以这样,相当简单
val numbers = listOf<Int>(1, 2, 3, 4)
filter(numbers) {
it % 2 == 1 // 奇数
}
从上面的例子我们可以看出方法参数的厉害和简洁了。
Kotlin
Lambda表达式
在Kotlin编写的过程中,会遇到大量的Lambda表达式,Lambda表达式是Java8中提出的一种表达式,对于Android用户来说,并没有相应的使用。kotlin为Android开发者提供了大量的现代语言编程方式与Java中新提到的概念与方法,Lambda表达式就是其中一种。
为什么?
我们为什么需要Lambda表达式
主要有三个原因:
> 更加紧凑的代码
比如Java中现有的匿名内部类以及监听器(listeners)和事件处理器(handlers)都显得很冗长
> 修改方法的能力(我个人理解为代码注入,或者有点类似JavaScript中传一个回调函数给另外一个函数)
比如Collection接口的contains方法,当且仅当传入的元素真正包含在集合中,才返回true。而假如我们想对一个字符串集合,传入一个字符串,只要这个字符串出现在集合中(忽略大小写)就返回true。
简单地说,我们想要的是传入“一些我们自己的代码”到已有的方法中,已有的方法将会执行我们传入的代码。Lambda表达式能很好地支持这点
> 更好地支持多核处理
例如,通过Java 8新增的Lambda表达式,我们可以很方便地并行操作大集合,充分发挥多核CPU的潜能。
并行处理函数如filter、map和reduce。
怎么做?
实例1 FileFilter
File dir =
new
File("/an/dir/"); FileFilter directoryFilter =
new
FileFilter() {
public
boolean
accept(File file) {
return
file.isDirectory(); }};
通过Lambda表达式这段代码可以简化为如下:
File dir =
new
File("/an/dir/");FileFilter directoryFilter = (File f) -> f.isDirectory();File[] dirs = dir.listFiles(directoryFilter);
进一步简化:
File dir =
new
File("/an/dir/");File[] dirs = dir.listFiles((File f) -> f.isDirectory());
Lambda表达式使得代码可读性增强了。我承认我开始学习Java的时候对那个匿名内部类感到很困扰,而现在Lambda表达式让这一切看起来都很自然(尤其是有.NET背景的童鞋会发现这个跟.NET中的Lambda表达式好像)
Lambda表达式利用了类型推断(type inference)技术:
编译器知道FileFilter只有一个方法accept(),所以accept()方法肯定对应(File f) -> f.isDirectory()
而且accept()方法只有一个File类型的参数,所以(File f) -> f.isDirectory()中的File f就是这个参数了,
.NET把类型推断做得更绝,如果上面用.NET Lambda表达式写法的话是这样的: File[] dirs = dir.ListFiles(f => f.isDirectory());即压根就不需要出现File类型指示。
实例2 Event Handler
Button bt =
new
Button(); bt.addActionListener(
new
ActionListener() {
public
void
actionPerformed(ActionEvent e) { ui.showSomething(); }});
使用Lambda表达式后:
Button bt =
new
Button();ActionListener listener = event -> { ui.showSomething(); };bt.addActionListener(listener);
进一步简化:
Button bt =
new
Button();bt.addActionListener(event -> { ui.showSomething(); });
外循环、内循环和Map、Reduce、Filter
一直到现在,处理Java集合的标准做法是采用外循环。比如:
List<String> list =
new
ArrayList<String>();list.add("hello");list.add("world");
for
(
int
item: list) {
// 处理item
}
还有迭代器循环,它们都是外循环,并且都是顺序处理(sequential handling)。顺序特性也常常引发ConcurrentModificationException,只要我们尝试着并发修改集合。
Lambda表达式提供了内循环机制。
我们工作中可能经常面临下面的需求:
> 过滤掉一个集合中不符合条件的元素得到一个新集合> 对集合中的每个元素进行某种转换,并且对转换后的集合进行处理> 统计整个集合的某个属性,比如统计集合元素值的总和或平均值
这些任务即filter、map和reduce,他们的共同特点是:
需要对集合中的每个元素运行一小段相同的代码。
传统的实现这些任务的代码让人感到很乏味,幸运的是Java 8提供了完成这些任务的更简洁的方案,当然还是利用Lambda表达式,但也引入了一个新的类库java.util.functions,包含Predicate、Mapper和Block。
Java 8中,一个Predicate(谓词)是这样一个方法:它根据变量的值进行评估(evaluate),返回true或false。
比如下面:
List<String> list = getMyStrings();
for
(String myString: list) {
if
(myString.contains(possible)) { System.out.println(myString + " contains " + possible); }}
使用Predicate和Filter后得到下面代码:
List<String> list = getMyStrings();Predicate<String> matched = s -> s.equalsIgnoreCase(possible);list.filter(matched);
进一步简化:
List<String> list = getMyStrings();list.filter(s -> s.equalsIgnoreCase(possible));
Lambda表达式语法规则
到目前为止Java 8中的Lambda表达式语法规则还没有完全确定。
但这里简单介绍下:
对于前面的:
File dir =
new
File("/an/dir/");File[] dirs = dir.listFiles((File f) -> f.isDirectory());
accept()方法返回布尔值,这种情况f.isDirectory()显然也得是布尔值。这很简单。
而对于:
Button bt =
new
Button();bt.addActionListener(event -> { ui.showSomething(); });
actionPerformed()方法的返回类型是void,所以需要特殊处理,即在ui.showSomething();左右加上花括号。(想象下不加会怎么样?如果不加的话,若showSomething()方法返回值是整数类型,那么就意味着actionPerformed()返回整数类型,显然不是,所以必须加花括号用来标记)。
如果Lambda表达式主体部分包含多条语句,也必须用花括号,并且return语句不能省。
比如下面这个:
File dir =
new
File("/an/dir/");File[] dirs = dir.listFiles((File f) -> { System.out.println("Log:...");
return
f.isDirectory(); });
使用三方控件遇到的坑与注意点
Databinding
使用Databinding需要在app moudle的build.gradle里面添加
kapt {
generateStubs = true
}
dependencies {
kapt 'com.android.databinding:compiler:2.2.0-rc2'
}
dependencies {
kapt 'com.android.databinding:compiler:2.2.0-rc2'
}
2.2.0-rc2 这个版本号必须要和根目录下的build.gradle文件内的com.android.tools.build:gradle:2.2.0-rc2 版本号一致
Dagger2
官方博客说是支持Dagger2的,但实际遇到的情况是和Databinding结合使用有问题
奇怪的报错
使用Databinding,设置ViewModel时
cannot access class '...'. check your module classpath for missing or conflicting dependencies
应该是Kotlin的bug,只是报错,不影响编译,如果不希望提示错误,可以添加
@Suppress("MISSING_DEPENDENCY_CLASS")
编译的时候报找不到class的问题,clean一下就好,不知道什么原因
怎么从Kotlin转为Java
两种方法
打包,输出apk,使用dex2jar将apk代码文件提取成jar,使用jd-jui查看,将对应的代码拷贝出来,修改,使用
把Kotlin文件删掉,用Java重写一遍
转载内容链接:
http://www.cnblogs.com/feichexia/archive/2012/11/15/Java8_LambdaExpression.html