一:变量的声明跟赋值
Java:
public View mTvName;
public String name="chencm";
public final int age = 1;
Kotlin:
var mTvName:View?=null
var name:String = "chencm" //var修饰参数可读可写,如果要修改值 直接name="cc"
var name = "chencm" // 像这种在声明变量的时候直接赋值,可以不写变量类型,这个是Kotlin的类型推断
val age = 1
lateinit var mHeadView:View //如果不想马上初始化,就可以使用lateinit
几点不同讲解:
- kotlin默认是public的,可以省略
- kotlin里声明变量使用var,val关键字
- kotlin变量名在前,变量类型在后,java则相反
- kotlin里变量名跟类型之间用“:”冒号隔开
- kotlin里变量必须要有初始化的值,java有默认
- kotlin末尾不需要写“;”分号
var修饰的变量是可读可写变量,比如上面的代码修改name的值,name="cc"
val修饰的变量是只能赋值一次,类似java的final
kotlin中如果声明变量直接赋值:比如var name = "ccm" 这种就是kotlin的类型判断,它会自动把name判断成字符串类型
kotlin里变量必须初始化,当我们想要使用的时候在初始化,那么可以使用lateinit,但在使用的时候,必须保证是初始化过的,例子:
lateinit var mHeadView:View //如果不想马上初始化,就可以使用lateinit
private fun initUI(){
// 举个例子:可以判断是否没初始化过
if(!::mHeadView.isInitialized){
mHeadView = new View()
}
// 必须保证mHeadView是初始化过的
mHeadView.bindData()
}
二:Kotlin空安全设计
Java:
//声明可空的变量
@Nullable
public View view = null;
// 使用可空的变量,判断非空
If(view!=null){
view.setBackgroundColor(Color.RED)
}
Kotlin:
//声明可空的变量
var view:View?=null
// 使用可空的变量,判断非空
view?.setBackgroundColor(Color.RED)
这种类型之后加"?"的写法,在 Kotlin 里叫可空类型 ?表示可空,!!表示非空,如果使用!!那么该变量一定不能为空,否则会报空指针,举例:
var student:Student?=null
student?.name = "ccm"//当student没有初始化过时,不会报空指针
student!!.name = "ccm"//当student没有初始化过时,报空指针
var myName = student?.name!!
一起讲下Kotlin的?:
Kotlin中的?:表示如果空则使用?:后面的值 继续上面的代码举例
var myName = student?.name?:"cc" //表示如果student为空,或者student.name为空的时候,就默认给myName赋值为cc
三:函数的声明
Java:
// 没有入参,没有返回参数
public void log(){
Log.e("xxx","xxx");
}
// 有入参,有返回参数的
public int sum(int x,int y){
return x+y;
}
Kotlin:
// 没有入参,没有返回参数
fun log():Unit{
Log.e("xx","xx")
}
fun log(){
Log.e("xxx","xxx")
}
// 有入参,有返回参数的
fun sum(x:Int,y:Int):Int{
return x+y
}
// 函数简化
fun sum(x:Int,y:Int) = return x+y
fun getStudent(teacher:Teacher?):Stuent?{
return null
}
几点不同讲解:
- kotlin默认是public的,可以省略
- kotlin是fun关键在声明函数
- kotlin里无返回值的函数,默认类型是Unit,Unit可以省略,Java是Void
- kotlin参数在前,参数类型在后,并且用":"冒号隔开,Java是相反
- kotlin返回值类型是在函数的右边并且用":"冒号隔开,Java是在前面
- kotlin返回类型,跟入参都可以是可空类型
- kotlin中函数可以简化return跟{} 直接用”=“号来代替
四:函数的重载
Java:
public int sum(int x){
return sum(x,0)
}
public int sum(int x,int y){
return x+y;
}
//调用
sum(1);
sum(1,1)
Kotlin:
fun sum(x:Int=0,y:Int=0):Int{
return x+y
}
//调用
sum()
sum(x=1)
sum(y=1)
sum(1)
sum(1,1)
Kotlin中可以通过对入参添加默认值,从而来实现函数的重载
五:类型
Java:
public int num = 1;//还有long,double,float,short,byte
// 当然Java还要装箱的
public Integer num = 1;//还有Long,Double,Float,Short,Byte
// java里float转int还得使用强转
public float x = 1.55f;
int y = (int)x;
public char c = 'c';
public Boolean flag = true;
public String name = "ccm";
// Java数组
public int[] array = new int[]{1,2} //还有long的数组,double数组,float数组,short数组,byte数组等类似
public String[] strArray = new String[]{"xx","cc"};
publci Student[] stuArray = new Student[10];
// Java常用集合
public List<Student> list = new ArrayList<Student>();
public Map<Student> map = new HashMap<Student>();
Kotlin:
// Kotlin是不管拆箱跟装箱统一用Int
var num:Int = 1 //还有Long,Double,Float,Short,Byte
// Kotlin里Int跟Float等的转化直接是有方法调用,比如:
var x = 1.55f
var y = x.toInt() //类似方法还有toFloat(),toDouble(),toLong(),toShort(),toByte()
var c:Char = 'c' // 当然这种直接赋值,由于kotlin类型推断都是可以省略变量类型的
var flag:Boolean = false
var name:String = "ccm"
// kotlin的数组,还有DoubleArrayOf,FloatArrayOf,LongArrayOf,ShortArrayOf,ByteArrayOf等
var array:IntArrayOf=intArrayOf(1,2)
var strArray:Array<String> = arrayOf("xxx","ccc")//也可以写成var strArray = arrayOf("xx","cc")
var studentArray:Array<Student> = arrayOf(Student("xx"),Student("xx1"))
// kotlin的集合
var list= ArrayList<Student>()
var map = HashMap<String,String>()
// kotlin的只读list,只读map,可变list,可变map
val list = listOf("a","b","c") //只读的list
val map = mapOf("a" to 1, "b" to 2, "c" to 3) //只读的map
// 可变集合 mutableMapOf修饰可变的map, mutableListOf是修饰可变的list, mutableSetOf是修饰可变的Set
val maps = mutableMapOf("name" to "ccm", "age" to "1")
maps.put("xxx","000")
// 不过不可变的集合是可以通过toMutable*()方法来变成可变集合的
// 比如上面的listof
list.toMutableList()//转化成了可变的集合
map.toMutableMap()//转化成可变的map,set同理
Java中的Object相当于Kotlin中的Any
六:类的继承跟实现
Java:
public class MainActivity extents Activity implements View.OnClickListener{
@Override
public void onClick(View v){
}
}
Kotlin:
class MainActivity:Activity(),View.OnClickListener{
override
fun onClick(v:View){
}
}
// 举例自定义个Apple类继承自定义水果类
open class Fruit{}
class Apple:Fruit(){}
Kotlin当中继承跟实现都是使用":"冒号,如果有多个的话,用","逗号隔开,Java继承用extends,实现用implements
Java中方法使用@Override注解,Kotlin是override关键字
Kotlin里类默认是final的,如果想要继承,那么可以用open修饰
七:类型判断跟强转
Java:
// 举个例子,判断水果如果是苹果的话,调用苹果的吃方法
public void test(Fruit fruit){
if(fruit instanceof Apple){
((Apple)fruit).eat();
}
}
Kotlin:
fun test(fruit:Fruit){
if(fruit is Apple){
(fruit as Apple).eat()
// 或者是
fruit.eat(); //因为类型判断原因,所以可以省略强转
}
// 或者上面的代码可以如下写法
(fruit as? Apple).eat()
// !is 表示不是某个类型
if(student !is Fruit){
xxx
}
}
Kotlin 中的is关键字相当于Java的instanceof
Kotlin 中的as关键字相当于Java的强转
Kotlin 中as? 表示如果强转成功就执行之后的调用,如果强转不成功就不执行
Kotlin 中!is表示不是某个类型
八:类的构造器
Java:
// Java中写一个类
public class HeadOverlayView extends LinearLayout{
public String name = "";
public HeadOverlayView(Context context){
this(context,null);
}
public HeadOverlayView(Context context, AttributeSet attrs){
this(context,attrs,0);
}
public HeadOverlayView(Context context, AttributeSet attrs,int defStyleAttr){
super(context,attrs,defStyleAttr);
name = "ccm";
}
}
Kotlin:
// Kotlin中写一个类
class HeadOverlayView : LinearLayout{
var name = ""
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr){
name = "ccm"
}
}
// 也可以这么写
class HeadOverlayView(context:Context):LinearLayout(context){}
Java中构造器的写法是:类名的形式
Kotlin构造器的写法有两种
次级构造器:使用关键字constructor声明的构造器是次级构造器
主构造器:直接在HeadOverlayView(context:Context) 后面写这种方式是主构造器方式
Kotlin中this的用法跟Java中一样,super也是,只是Kotlin次级构造参数后面是用:接着调用super或者this,而Java是写在{}中
九:init
Java:
public class Student{
{
//做点事情
}
public Student(){}
}
Kotlin:
class CustomView(context:Context):LinearLayout{
init{
LayoutInflater.from(context).inflate(R.layout.layout_customview, this)
}
}
Kotlin中经常在init{}里去写一些初始化的代码,等价于Java的{} 都是在实例化时执行,并且都在构造器之前调用
十:object
object是Kotlin的关键字,并非Java的Object,Java的Object是表示所有类的基类,Kotlin中表示所有类的基类用的是Any
object关键在,在Kotlin中的意思是,创建一个类,并且创建一个这个类的对象,他是生成一个单例对象
Java:
public class Test {
private static Test sInstance;
public static Test getInstance() {
if (sInstance == null) {
sInstance = new Test();
}
return sInstance;
}
public void add(){
}
}
// 调用代码
Test.getInstance().add()
// 饿汉式
public class Test{
private Test(){}
private static final Test test = new Test();
public static Test getInstance(){
return test;
}
}
Kotlin:
object Test {
var name = "ccc"
fun add(){}
}
// 调用
Test.add()
Test.name
通过object 实现的单例是个线程安全的,饿汉似的单例。 当我们想要使用单例,或者想要通过类名.的方式去调用某个类里面的变量跟方法的时候,可以用object
十一:伴生对象companion object
在Java里我们在类里面写一些静态常量,静态方法,让类直接就可以调用,而在Kotlin中我们就可以用伴生对象了
Java:
public class VideoDetailActivity extends Activity{
public static final String EXTRA_ID = "id";
public static final int MAX_COUNT = 700;
public static void enter(Context context,String id){
Intent intent = new Intent(context,VideoDetailActivity.class);
intent.putExtra(EXRTA_ID,id);
context.startActivity(intent);
}
// 静态代码块
static{
}
}
// 调用
VideoDetailActivity.enter(context,"1");
Kotlin:
class VideoDetailActivity:Activity(){
companion object {
const val EXTRA_ID = "id"
const val MAX_COUNT = 700
fun enter(context:Context,id:String){
val intent = Intent(context,VideoDetailActivity::class.java).apply{
putExtra(EXRTA_ID,id)
}
context.startActivity(intent)
}
// 类似java的静态代码块
init{
}
}
}
// 调用
VideoDetailActivity.enter(context,"1")
当我们只想让类中的一部分函数和变量是静态的,我们就可以用companion object
这里顺便讲几个点:
- Java中声明常量用static final ,而Kotlin中用const val
- Java中创建一个对象是用new关键字,而Kotlin不需要new,比如Java:new Intent(),Kotlin是Intent()
- Kotlin中拿到java的class对象是使用 类名::class.java的形式,例如VideoDetailActivity::class.java,
- Kotlin中新建一个对象之后,要对对象里面的属性赋值,可以通过apply{} 并在{}里对属性进行初始化 举例:
Java:Kotlin:Student stu = new Student(); stu.name = "ccm";Student().apply{ this.name = "ccm" }
十二:匿名内部类
Java:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
Kotlin:
button.setOnClickListener(object:View.OnClickListener{
override fun onClick(v: View?) {
}
})
// 通过lambda改写
button.setOnClickListener({v:View->
})
// 如果lambda是函数的最后一个参数,那么可以把Lambda写在()小括号外面
button.setOnClickListener(){v:View->
}
// 如果Lambda的函数是唯一的参数,可以把()小括号去掉
button.setOnClickListener{v:View->
}
// 如果这个Lambda是单个参数的,那么参数也可以省略,表达式可以简写成
button.setOnClickListener{
}
Kotlin写匿名内部类的时候,是通过object: 写的
十三:可见性修饰符
Java:
// java中有public ,表示都可见
public class User{
public String name;
public void method(){}
}
// java中没有写修饰符表示包内可见default
class User{} //同包名下才可见
// protected 在java中表示包内可见+子类可见
public class User{
protected void walk(){
}
}
// private 在java中表示类中可见,作为内部类是对外部类可见
public class Test{
private int type =1;
public void test(){
Inner inner = new Inner();
inner.name = "xxx";//访问的到
}
private class Inner{
private String name = "cc";
}
}
Kotlin:
// Kotlin不写默认就是public ,表示都可见
class User{
var name:String;
fun method(){}
}
// kotlin中internal修饰付表示对module内可见,Kotlin里没有Java那样的包内可见的修饰符了
internal class User{} //表示这个类只能被同module下的可见
// protected在kotlin中就表示private+子类可见
// private表示类中或所在文件内可见,作为内部类时,对外部类不可见
class Test{
// 这个是只在Test这个类里可见
private val type = 1
public void test(){
val inner = Inner()
inner.name //这个代码访问不到
}
private class Inner{
private var name = "xx"
}
}
// 这个是表示只在Test这个文件里可见(这个也是顶层变量)
private val name = "xx"
Kotlin中的public表示对可见
internal表示包内可见
protected表示private+子类可见
private表示类中可见或文件里可见,作为内部类时,外部类不可访问到
十四:when,if,if else
Java:
// java中使用switch
public int test(int type){
int result = 0;
switch (type){
case 1:
result = 1;
break;
case 2:
case 4:
result = 6;
break;
case 8:
result = 10;
break;
default:
result = 20;
break;
}
return result;
}
// java中使用if else
public int test(int type){
int result = 0;
if(type == 1){
result = 1;
}else if(type == 2 || type == 4){
result = 6;
}else if(type == 8){
result = 10;
}else{
result = 20
}
return result;
}
Kotlin:
fun test(type:Int):Int{
val result = when(type){
1->1
2,4->6
8->10
else->20
}
return result
}
// 当然kotlin也可以使用if else的形式,跟java没有太大区别,
fun test(type:Int):Int{
val result = if(type==1) 1
else if(type ==2 || type == 4) 6
else if(type == 8) 10
else 20
reutrn reuslt
}
Kotlin中if else使用跟java没有太大区别,唯一区别是kotlin中的if else可以有返回值,因为在Kotlin中函数其实中可以作为一个对象
Kotlin中的when基本替代了Java的switch的用法
Kotlin中没有了switch这种用法
十五:字符串拼接
Java:
// java字符串的拼接
public String name = "xxx"
public String text = "昵称"+name
// 使用String.format拼接
System.out.print(String.format("昵称 %s", name));
Kotlin:
// kotlin字符串的拼接
val name = "xxx"
var text = "昵称"+name
// 使用$拼接
println("昵称$name");
十六:== 和 ===
Java:
// java中== 如果是用在基本数据类型,那么是判断值是否相等,如果是放在字符串Stirng是表示引用地址是否相等,Java中判断字符串值是否相等用equals
public int num = 1;
if(num==1) //true
public String s1 = "xx";
public String s2 = "xx";
if(s1 == s2) // false
if(s1.equals(s2)) //true
Kotlin:
Kotlin中==是判断值是否相等,如果是===则判断引用地址是否相等,当然Kotlin也可以用equals
val num = 1
if(num==1)
val s1 = "xx"
val s2 = "xx"
if(s1==s2) //等价于 if(s1.equals(s2))
if(s1===s2) //这个是判断引用地址是否相等
十七:数组,集合的遍历
Java:
// 数组遍历
String[] strs = new String[]{"xx","xxx","xxxxx"};
for(String str:strs){
...
}
// 集合遍历
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
for(int i=0;i<list.size;i++){
list.get(i);
.....
}
for(int index:list){
.....
}
Map<Stirng,String> map = new HashMap<String,String>();
map.put("xx","xxxx");
map.put("xx1","xxxxx");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
Kotlin
// 数组遍历
val strs = arrayOf("xx","xxx","xxx")
for(str in strs){
...
}
// 集合遍历
val list = listOf(1,2,3)
for(i in 0 until list.size){
.....
}
for(item in list){
....
}
// 甚至Kotlin中遍历可以跳指定step
// 下面意思是0,接着是2...
for(i in 0 until list.size step 2){
...
}
// 更经常用的是
list.forEach{
// it就是指集合里面的值
println(it)
}
list.forEachIndex{index,i->
// index是位置索引,i是list里index索引下具体的值
}
// Map的话
var map = mapOf<String,String>("name" to "ccm","b" to "cjh")
for ((k, v) in map) {
println("$k -> $v")
}
val value = map.get("a")//这个是访问map
map.forEach {
// 这里的it值Map.Entry<String,String>
it.key
it.value
}
// 一起讲一下kotlin中的while,continue,break用法跟java的没有什么区别
var x = 6
while (x > 0) {
x--
if (x == 4) {
continue
}
if (x == 2) {
break
}
Log.e("ccm", "x=$x")
}
输出结果是 5,3
十八:Kotlin不需要findViewById
Java:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_back"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginLeft="10dp"
android:scaleType="centerInside"
android:src="@drawable/nav_btn_back"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
public class TestActivity extends Activity{
private ImageView mBackIv;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test);
mBackIv = (ImageView) findViewById(R.id.iv_back);
mBackIv.setImageResource(R.drawable.xxx);
}
}
Kotlin:
// 需要在app的build.gradle里加入 插件 apply plugin: 'kotlin-android-extensions'
// 然后在使用的地方导入对应的布局的包,就不需要findViewById
import kotlinx.android.synthetic.main.activity_test.*
class TestActivity : Activity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
iv_back.setImageResource(R.drawable.xxx)
}
}
十九:顶层声明
Kotlin的顶层声明的函数跟变量是属于package的。在同一包名内,都可以直接调用,不是同一包名需要引入包。举例:比如我们在package com.menstrual.video包名下建一个video.kt文件
package com.menstrual.video
const val HOST = "http://www.baidu.com"
// 比如我们可以写个router跳转
fun router(path: String): Any? {
return ARouter.getInstance().build(path).navigation()
}
// 写个字符串空判断
fun isEmpty(source: String?): Boolean {
return !TextUtils.isEmpty(source)
}
// 这样在同包名下的任何地方都可以直接调用上面两个方法,跟获取到HOST
class TestActivty:Activity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 输出顶层变量HOST
Log.d("info","==host==${HOST}")
// 判断是不是等于空
if(isEmpty("")){
// 调用router跳转页面
router("/circle/video")
}
}
}
// 如果在不同包名下的话,那么也是可以使用的只不过要导入相应的包引用
package com.menstrual.test
import com.menstrual.video.router
import com.menstrual.video.isEmpty
class TestActivty2:Activity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 判断是不是等于空
if(isEmpty("")){
// 调用router跳转页面
router("/circle/video")
}
}
}
二十:扩展函数
Kotlin中的扩展函数是可以给具体的类,添加扩展的函数。举例: com.menstrual.video包名下建一个video.kt文件,在这个video.kt里添加扩展函数
package com.menstrual.video
// 给字符串String添加扩展函数
fun String.cut():String= this?.substring(0,1)
// 在同包名下的任何类都可以调用
val name = "cxk"
// 就可以让字符串调用我们自己写的cut方法,这个cut方法就是扩展函数
val str = name.cut()
// 给View添加扩展函数
fun ImageView.loadImage(url:String,placeResId:Int){
Glide.with(context).load(url).placeholder(placeResId).into(this)
}
// 调用 比如mHeadView是个ImageView
mHeadView.loadImage("http://xxx",R.drawable.xxx)
// 如果在不同包名下要调用的话,也是导入对应的包就行了,举例
package com.menstrual.test
import com.menstrual.video.loadImage
class TestActivty:Activity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ivHead.loadImage("http://xxx",R.drawable.xxx)
}
}
二十一:内联函数
Kotlin中声明内联函数关键字是inline
// 这个是kotlin源码里 String.kt 判断字符串是否为空的代码
public inline fun CharSequence.isEmpty(): Boolean = length == 0
// 调用
val text = "xxx"
if(text.isEmpty())
为何要写内联函数?
- 我们每调用一个方法的时候,都会创建出一个栈帧,并且把这个栈帧压入到方法栈,当方法调用完成之后又需要把该栈帧出栈,这整个调用方法的过程是会消耗资源的。
- 我们在开发过程当中,我们会把我们经常使用到的代码抽成方法,而这些方法是我们经常调用的,比如判断字符串是否为空类似的方法,那么这时候是比较耗资源的。
- 我们使用内联函数之后,编译之后,我们方法调用的地方,都会被替换成方法的内容,这样就不用去创建栈帧了,因为在编译时候自动做的事情,所以不影响我们开发时候使用。从而减少了资源的消耗,同时不影响使用。
// 我们方法调用的地方,内联函数在编译后被替换成方法内容,什么意思呢?举例: 比如上面的 text.isEmpty() 因为是内联函数编译后代码text.length == 0, 这个方法isEmpty() 直接被替换成了方法内容length==0,所以这样就少了栈帧的资源消耗
所以这就是我们使用内联函数的原因。如果很多函数经常被使用到,那么我们可以把这个方法定义为内联函数。
二十二:高阶函数
Kotlin中的高阶函数,是如果方法的参数是函数类型,或者方法的返回值是函数类型,那么我们就把这个函数成为高阶函数,举例:
fun getName(name:String):String = "my name is ${name}"
// 参数是函数类型的例子 method:(name:String)->String 这个是表示参数是String,并且返回类型是String的函数
fun join(s1:String,method:(name:String)->String):String{
return method(s1)
}
// 调用
fun test(){
// 调用结果输出是 my name is ccm
println(join("ccm",::getName))
}
// 返回类型是函数类型的例子 (Int)->Unit 这个表示参数是int无返回类型的函数
fun test2(num: Int): (Int) -> String {
...
}
Kotlin中的双冒号“::”表示函数引用,对于一个声明好的函数,当我们要把它作为参数传递给函数,还是要把它赋值给变量,都得在函数名的左边加上双冒号,来进行调用,举例
fun getName(name:String):String = "my name is ${name}"
// 把声明好的函数getName,赋值给变量
val b = ::getName
// 把声明好的函数,作为参数调用
join("cc",::getName)
二十三:嵌套函数
Kotlin中可以在函数里面写函数,这种就是嵌套函数
// 比如
fun main(){
fun test(){
xxx
}
//调用,test方法只属于main方法
test()
}