Kotlin教程笔记(29) -Kotlin 兼容 Java 遇到的最大的“坑”

简介: Kotlin教程笔记(29) -Kotlin 兼容 Java 遇到的最大的“坑”

本系列学习教程笔记属于详细讲解Kotlin语法的教程,需要快速学习Kotlin语法的小伙伴可以查看“简洁” 系列的教程

快速入门请阅读如下简洁教程:
Kotlin学习教程(一)
Kotlin学习教程(二)
Kotlin学习教程(三)
Kotlin学习教程(四)
Kotlin学习教程(五)
Kotlin学习教程(六)
Kotlin学习教程(七)
Kotlin学习教程(八)
Kotlin学习教程(九)
Kotlin学习教程(十)

Kotlin教程笔记(29) -Kotlin 兼容 Java 遇到的最大的“坑”

Kotlin 兼容 Java 遇到的最大的“坑”

前言:上周我发了一篇文章Kotlin 遇到 MyBatis:到底是 Int 的错,还是 data class 的错?讲如何解决群里面一兄弟遇到的 data class 与 MyBatis 相克的问题,其中提到了几种外门邪道的方法,也提到了官方的解决思路,有些朋友看了之后还是不太明白,甚至紧接着就有小伙伴在使用 Realm 的时候遇到了类似的问题,看来,我还是得再写一篇来进一步告诉大家,这究竟是个什么问题,以及该如何面对它。

一个 Realm 的小例子

Realm 在 2016 年与 RxJava、Retrofit 这样的框架一起,在 Android 开发领域内着实小小的火了一把,如果大家对它不了解,没关系,传送门 biu ~ Realm

我们先按照官网的说明配置好 gradle 依赖,话说呀,这互联网发展这么快,新时代的框架一出来,逼格果断就体现在完善的构建和开发生态,你发布的东西还只是一个 jar 包,人家呢,早上了 maven 不说,还要搞几个 gradle 任务来方便你开发:

buildscript {
    ext.kotlin_version = '1.1.1'
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.0'
        //这里将 realm 的 gradle 插件加入 gradle 构建的运行时
        classpath "io.realm:realm-gradle-plugin:3.0.0"
    }
}
...
apply plugin: 'com.android.application'
apply plugin: 'realm-android' //应用插件,Realm 会在这里添加自己的一些构建任务
...

其实 gradle 插件开发也是一个很有意思的话题,如果大家有需要,我后面也可以写几篇文章介绍下(悄悄告诉你们,其实我早就想写了,这不是 kotlin 版的 gradle 还没有正式发布么!)。

有了这个我们就可以开始写个 Realm 的 demo 了。

小明:等等!我还有一事不明,你怎么不添加 realm 的依赖就要开始写 demo 了啊!

艾玛,要么说小明人家就是明白人呢,我前面写了一大堆,只不过是添加了 gradle 构建的依赖而已,而我们的程序想要使用 realm,必须依赖 realm 的运行时库才行。那这么说我是不是漏掉了什么?当然没有,怎么会呢,我这么聪(dou)明(bi)的人,我可是一步一步照着官网的步骤抄的!

其实呀,realm 的运行时依赖早在我们 apply plugin 的时候就已经添加进来的, realm-android 这个插件除了添加了一些它需要的 gradle 任务之外,也顺手帮我们把依赖添加了。嗯,就是酱紫,如果有那个同学学(xian)有(de)余(dan)力(teng),可以翻一翻 realm 插件的源码。

来来来,赶紧看 demo,不然有些人该内急了~

首先在 Application 当中初始化它:

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        Realm.init(this)
        Realm.setDefaultConfiguration(
                RealmConfiguration.Builder()
                .deleteRealmIfMigrationNeeded()
                .schemaVersion(1)
                .build())
    }
}

定义一个 User 类:

data class User(@PrimaryKey var id: Int, val name: String) : RealmObject()

接着我们开始存数据和查数据啦:

add.setOnClickListener {
    Realm.getDefaultInstance().use {
        it.beginTransaction()
        val d = it.createObject(User::class.java, it.where(User::class.java).count())
        d.name = "User ${d.id}"
        it.commitTransaction()
    }

}

query.setOnClickListener {
    Realm.getDefaultInstance().use {
        it.where(User::class.java).findAll().map {
            Log.d(TAG, it.toString())
        }
    }
}

想得挺美,结果呢?编译不通过。

Error:A default public constructor with no argument must be declared in User if a custom constructor is declared.

无参构造方法

这就让我想到上周的文章,那篇文章里面我们其实就发现症结根本不是什么 Int 和 Integer,而是无参构造方法。JavaBean 是 Java 的一个概念,我其实甚至有些觉得 Java 的设计者们通过 JavaBean 这样的概念来弥补语言本身的缺陷——不管怎样,JavaBean 是不能没有无参构造的,。

Kotlin 呢,语言层面就有类似于 JavaBean 的东西,那就是 data class,这俩孩子实在太像了,以至于大家经常把 data class 当做 JavaBean 来使。嗯,你信不信 Kotlin 的设计者也是这么想的呢?当然,用 data class 这样一个名正言顺的“亲儿子”数据类来替代 JavaBean 这么个语言层面没有任何支持和认可的“野孩子”,应该算是 JavaBean 莫大的荣幸了,可问题又出在 Java 语言本身构造方法滥用的潜在问题上了。在 Java 中,构造方法真心是一个很没有存在感的东西,大家总是根据自己的喜好来随意的定义很多个构造方法的版本,而最终忽视掉它们的内在联系,导致没有正常走完初始化逻辑的实例满天飞,这家伙如果是导弹,我估计也不需要解放军就可以直接把台湾给统一了。

说了这么多,我主要是想吐槽两个点:第一个就是 Java 本身语言设计层面几乎没有任何照顾到数据类的体现(可千万别说 clone 和 Serialize),第二个就是 Java 对其对象的实例化过程的把控太过于儿戏。

这两点呢,Kotlin 都做的很好,我现在写 Kotlin 经常被迫认真思考一个类该如何正确初始化,这显然对于我们的程序结构和逻辑梳理有莫大的好处。可是结果呢?Java 时代的那些框架们受不了了。Kotlin 背靠着 Java 这座大山,Java 就像它的父母一样,父母的观念再老再陈旧,Kotlin 也得做好自己该做的,一方面是向现在看来陈旧但在过去已经非常革命的观念致敬,另一方面嘛,如果 Java 不支持个几十万首付,Kotlin 能买得起房吗?

哇塞,我好能扯啊。

其实想要解决 default public constructor 这样的问题,Kotlin 官方已经想到了,那就是 noarg。嗯,我原以为我提一句 noarg 大家就会知道是什么了,看来是我想的简单了,毕竟这个东西在 1.0.6 才出来,当时我还在介绍这个版本的时候提到了它的使用方法,朋友们可能还没有接触过,没关系,下面我再贴一些写法,大家一看就明白:

首先你要做的就是定义一个注解:

annotation class PoKo

接着 gradle 配置一下脚本的依赖:

buildscript {
...
    dependencies {
        ...
        classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
        ...
    }
}

加了运行时环境,那么我们就可以使用 noarg 插件了:

apply plugin: "kotlin-noarg"

noArg {
    annotation("net.println.kotlin.realm.PoKo")
}

配置完之后,PoKo 这个注解就有了超能力,所有被它标注的类在编译时都会生成一个无参的构造方法,于是我们给 User 加一个 PoKo 的注解:

@PoKo data class User(...) : RealmObject()

搞定,果断去编译一下!!

final 还是不 final,这是个问题

本来兴高采烈的以为不就是个无参构造的问题嘛,结果编译的时候又爆出了新的问题:

Error:(31, 61) error: cannot inherit from final User

好家伙,这究竟发生了什么。。原来 Realm 在编译的时候生成了一个类:

public class UserRealmProxy extends net.println.kotlin.realm.User
    implements RealmObjectProxy, UserRealmProxyInterface

这个类要继承我这个 User 类,结果就报错了。

下面是理(che)论(dan)时间。我们说在 C++ 当中给合适的变量、函数参数、函数返回值甚至函数加上 const 是个好习惯,大家没有意见吧?同样的,Java 当中给那些不变的量、不能被继承的类、不能被覆写的方法加上 final 也是个好习惯,大家也没有意见吧?那么问题来了,大家有几个人这么干了?是不是不到万不得已,才懒得写那个 final 呢,五个字母呢,你是想累死宝宝啊?我就知道 Effective Java 这本书看了也白看,因为大家经常明知道什么是好习惯却还是要对着干,这个不是因为大家不喜欢好习惯,而是因为坚持好习惯需要成本!不瞒各位说,我中午为了坚持午休的好习惯,牺牲了跟组里面的小伙伴一起开黑上分的机会,还得装着拥护“人民ri报”关于“小学生打排位太坑”的评论,我容易么我。。

嗯,扯远了。还是说 final 的事儿,Kotlin 就做的很好,它默认所有的类、变量、方法都是 final 的,想要继承?来,过来申请我给你审批。。。你看,这样从根儿解决问题,我们再也不用为了坚持好习惯而发愁了,因为我们根本不需要坚持,难道你想要坚持坏习惯嘛?

可是 Java 及其框架们呢?原来到北京买房有钱就行,现在呢,商住都不让买了啊(什么?你说广州都不让卖了?)。那叫一个不适应,这可不是得闹事儿么。

Kotlin 官方考虑到 Java 帮它出首付买房的事儿,想了想算了,还是出个什么插件,解决下这个问题吧,于是 allopen 闪亮登场!allopen 的原理跟 noarg 极其类似,它是在编译器对指定的类进行去“final”化,你别看你写代码的时候 User 还是个 final 的类,不过编译成字节码之后这天呀可就变了。

关于 allopen 的使用,跟 noarg 简直不要太像,先定义一个注解:

annotation class PoKo // How old r U!

可以跟 noarg 公用同一个注解,也可以自己另外单独定义一个,这个不要紧。

接着 gradle 配置搞起:

buildscript {
    ...
    dependencies {
        ...
        classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
        ...
    }
}

接着就是应用插件,配置注解一气呵成:

apply plugin: "kotlin-allopen"

allOpen {
    annotation("net.println.kotlin.realm.PoKo")
}

编译运行~

ps:如果加了 allopen 和 noarg 之后编译仍然提示原来的错误,记得狠狠地 clean 一下才行哈。

认真脸:究竟什么是 “坑”

前面说了 Kotlin 的两个 “坑”,都是关于 data class 的。有人认为这么说 Kotlin 不公平,毕竟人家 Kotlin 也是可以写出下面的代码的:

class User{
    var id: String? = null
    var name: String? = null
}

尽管你在为 Kotlin 打抱不平,不过如果你真要写这样的代码,我建议你还是用 Java 吧。你不属于 Kotlin。。。

Kotlin 这么美的语言,怎么能写这么丑陋的东西呢?这就好比有人说为什么空类型强转为非空类型一定要两个感叹号呢,用一个不就够了么,两个看起来好丑呀!

var user: User? = getUser()
user!!.name = "小明" //小明,他们说你丑!

有人回答说:明明这就是丑陋的东西,为什么要美化?掩盖事物的本质只能让事情变得更糟糕!

我们用 Kotlin 企图兼容 Java 的做法,本来就是权宜之计,兼容必然带来新旧两种观念的冲突以及丑陋的发生,这么说来,我倒是更愿意期待 Kotlin Native 的出现了。

相关文章
|
7天前
|
人工智能 自动驾驶 大数据
预告 | 阿里云邀您参加2024中国生成式AI大会上海站,马上报名
大会以“智能跃进 创造无限”为主题,设置主会场峰会、分会场研讨会及展览区,聚焦大模型、AI Infra等热点议题。阿里云智算集群产品解决方案负责人丛培岩将出席并发表《高性能智算集群设计思考与实践》主题演讲。观众报名现已开放。
|
23天前
|
存储 人工智能 弹性计算
阿里云弹性计算_加速计算专场精华概览 | 2024云栖大会回顾
2024年9月19-21日,2024云栖大会在杭州云栖小镇举行,阿里云智能集团资深技术专家、异构计算产品技术负责人王超等多位产品、技术专家,共同带来了题为《AI Infra的前沿技术与应用实践》的专场session。本次专场重点介绍了阿里云AI Infra 产品架构与技术能力,及用户如何使用阿里云灵骏产品进行AI大模型开发、训练和应用。围绕当下大模型训练和推理的技术难点,专家们分享了如何在阿里云上实现稳定、高效、经济的大模型训练,并通过多个客户案例展示了云上大模型训练的显著优势。
|
27天前
|
存储 人工智能 调度
阿里云吴结生:高性能计算持续创新,响应数据+AI时代的多元化负载需求
在数字化转型的大潮中,每家公司都在积极探索如何利用数据驱动业务增长,而AI技术的快速发展更是加速了这一进程。
|
18天前
|
并行计算 前端开发 物联网
全网首发!真·从0到1!万字长文带你入门Qwen2.5-Coder——介绍、体验、本地部署及简单微调
2024年11月12日,阿里云通义大模型团队正式开源通义千问代码模型全系列,包括6款Qwen2.5-Coder模型,每个规模包含Base和Instruct两个版本。其中32B尺寸的旗舰代码模型在多项基准评测中取得开源最佳成绩,成为全球最强开源代码模型,多项关键能力超越GPT-4o。Qwen2.5-Coder具备强大、多样和实用等优点,通过持续训练,结合源代码、文本代码混合数据及合成数据,显著提升了代码生成、推理和修复等核心任务的性能。此外,该模型还支持多种编程语言,并在人类偏好对齐方面表现出色。本文为周周的奇妙编程原创,阿里云社区首发,未经同意不得转载。
11730 12
|
12天前
|
人工智能 自然语言处理 前端开发
100个降噪蓝牙耳机免费领,用通义灵码从 0 开始打造一个完整APP
打开手机,录制下你完成的代码效果,发布到你的社交媒体,前 100 个@玺哥超Carry、@通义灵码的粉丝,可以免费获得一个降噪蓝牙耳机。
5382 14
|
19天前
|
人工智能 自然语言处理 前端开发
用通义灵码,从 0 开始打造一个完整APP,无需编程经验就可以完成
通义灵码携手科技博主@玺哥超carry 打造全网第一个完整的、面向普通人的自然语言编程教程。完全使用 AI,再配合简单易懂的方法,只要你会打字,就能真正做出一个完整的应用。本教程完全免费,而且为大家准备了 100 个降噪蓝牙耳机,送给前 100 个完成的粉丝。获奖的方式非常简单,只要你跟着教程完成第一课的内容就能获得。
9581 15
|
1月前
|
缓存 监控 Linux
Python 实时获取Linux服务器信息
Python 实时获取Linux服务器信息
|
17天前
|
人工智能 自然语言处理 前端开发
什么?!通义千问也可以在线开发应用了?!
阿里巴巴推出的通义千问,是一个超大规模语言模型,旨在高效处理信息和生成创意内容。它不仅能在创意文案、办公助理、学习助手等领域提供丰富交互体验,还支持定制化解决方案。近日,通义千问推出代码模式,基于Qwen2.5-Coder模型,用户即使不懂编程也能用自然语言生成应用,如个人简历、2048小游戏等。该模式通过预置模板和灵活的自定义选项,极大简化了应用开发过程,助力用户快速实现创意。
|
5天前
|
机器学习/深度学习 人工智能 安全
通义千问开源的QwQ模型,一个会思考的AI,百炼邀您第一时间体验
Qwen团队推出新成员QwQ-32B-Preview,专注于增强AI推理能力。通过深入探索和试验,该模型在数学和编程领域展现了卓越的理解力,但仍在学习和完善中。目前,QwQ-32B-Preview已上线阿里云百炼平台,提供免费体验。
|
13天前
|
人工智能 C++ iOS开发
ollama + qwen2.5-coder + VS Code + Continue 实现本地AI 辅助写代码
本文介绍在Apple M4 MacOS环境下搭建Ollama和qwen2.5-coder模型的过程。首先通过官网或Brew安装Ollama,然后下载qwen2.5-coder模型,可通过终端命令`ollama run qwen2.5-coder`启动模型进行测试。最后,在VS Code中安装Continue插件,并配置qwen2.5-coder模型用于代码开发辅助。
911 5