//Code to be added
kotlinOptions {
allWarningsAsErrors = true
}
那么问题可能就会被提出来,开启这一选项有什么好处呢,毕竟我需要修改很多文件。
通常情况下,开启后的作用,我们可以归纳为如下
发现更多的潜在问题和崩溃
减少不必要的代码(变量,参数)
发现不好的编码实践
发现更多的API弃用问题
最终增加代码的健壮性和优雅程度
如下,我们会通过一些实践来说明一些问题
Nothing to Inline(作用不大的内联)
1
2
3
4
5
6
7
8
9
@Suppress("NOTHING_TO_INLINE")
inline fun String?.isNotNullNorEmpty(): Boolean {
//Expected performance impact of inlining
// 'public inline fun String?.isNotNullNorEmpty(): Boolean
// defined in com.example.warningsaserrorscases in file NothingToInlineWarnings.kt'
// is insignificant.
// Inlining works best for functions with parameters of functional types
return this != null && this.isNotEmpty()
}
public class RequestManager {
public static RequestManager sInstance = new RequestManager();
private static class TimelineRequest {
public String from;
}
public TimelineRequest getTimelineRequest() {
return new TimelineRequest();
}
}
1
2
3
4
5
6
7
fun testInaccessibleType() {
//Type RequestManager.TimelineRequest! is inaccessible in this context
// due to: private open class TimelineRequest defined
// in com.example.warningsaserrorscases.RequestManager
@Suppress("INACCESSIBLE_TYPE")
RequestManager.sInstance.timelineRequest
}
上述的testInaccessibleType无法访问TimelineRequest的属性和方法
具体的解决办法,可以是设置TimelineRequest为public,而非private
必要时可以使用@Suppress("INACCESSIBLE_TYPE")压制警告
UNCHECKED_CAST(未检查的类型转换)
1
2
3
4
5
fun <T> Any.toType(): T? {
//Unchecked cast: Any to T
@Suppress("UNCHECKED_CAST")
return this as? T
}
上面this as? T属于未检查的类型转换,可能在运行时抛出转换异常
不推荐使用@Suppress("UNCHECKED_CAST")压制警告
推荐使用reified方式处理
1
2
3
4
5
6
7
8
//a better way
inline fun <reified T> Any.toType(): T? {
return if (this is T) {
this
} else {
null
}
}
fun testEnum1() {
//Enum argument can be null in Java, but exhaustive when contains no null branch
when(SeasonUtil.getCurrentSeason()) {
Season.SPRING -> println("Spring")
Season.SUMMER -> println("Summer")
Season.FALL -> println("Fall")
Season.WINTER -> println("Winter")
//else -> println("unknown")
}
}
fun testEnum2() {
//Enum argument can be null in Java, but exhaustive when contains no null branch
@Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA")
when(SeasonUtil.getCurrentSeason()) {
Season.SPRING -> println("Spring")
Season.SUMMER -> println("Summer")
Season.FALL -> println("Fall")
Season.WINTER -> println("Winter")
}
}
interface OnViewClickedListener {
fun onViewClicked(viewId: Int)
}
fun testParameterNameChangedOnOverride() {
// The corresponding parameter in the supertype 'OnViewClickedListener'
// is named 'viewId'.
// This may cause problems when calling this function with named arguments.
object : OnViewClickedListener {
override fun onViewClicked(@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") id: Int) {
println("onViewClicked id=$id")
}
}
}
出问题的情况是当我们使用具名变量指定参数值时,可能出问题。
建议方法参数与源方法保持一致。
不建议压制警告
Name shadowing(命名遮挡)
1
2
3
4
5
6
7
fun testNameShadowing(message: String) {
run {
//Name shadowed: message
@Suppress("NAME_SHADOWING") val message = "Hello World"
println(message)
}
}
fun testUnnecessarySafeCall(message: String) {
@Suppress("UNNECESSARY_SAFE_CALL")
println(message?.toIntOrNull())
}
上述的安全调用其实是显得多余,因为Kotlin内部会有Intrinsics做参数非空的与判断
另外安全调用会增加if条件检查
建议主动移不必要的安全调用
不建议压制警告
SENSELESS_COMPARISON(无意义的比较)
1
2
3
4
5
6
7
fun testSenselessComparison(message: String) {
//Condition 'message != null' is always 'true'
@Suppress("SENSELESS_COMPARISON")
if (message != null) {
}
}
和前面的例子一样,这种检查是多余的,因为Kotlin内部会有Intrinsics做参数非空的与判断
建议主动移除无意义的比较
不建议压制警告
UNNECESSARY_NOT_NULL_ASSERTION(不需要的非空断言)
1
2
3
4
5
6
fun testUncessaryNotNullAssertion(message: String) {
//Unnecessary non-null assertion (!!) on a non-null receiver
// of type String
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
println(message!!.toIntOrNull())
}
这种断言是多余的,因为Kotlin内部会有Intrinsics做参数非空的与判断
建议主动移除不需要的非空断言
不建议压制警告
USELESS_IS_CHECK(没有用的实例类型检查)
1
2
3
4
5
6
7
fun testUselessIsCheck(message: String) {
//Check for instance is always 'true'
@Suppress("USELESS_IS_CHECK")
if (message is String) {
}
}
没有意义的类型检查,因为Kotlin内部会有Intrinsics做参数非空的与判断
建议主动移除不必要的检查
不建议压制警告
VARIABLE_WITH_REDUNDANT_INITIALIZER(变量初始化多余)
1
2
3
4
5
6
fun testVariableWithRedundantInitializer() {
//Variable 'message' initializer is redundant
@Suppress("VARIABLE_WITH_REDUNDANT_INITIALIZER") var message: String? = null;
message = System.currentTimeMillis().toString()
println(message)
}
建议手动移除多余的初始化
不建议压制警告
Deprecation (方法弃用)
1
2
3
4
fun testGetDrawable(context: Context) {
@Suppress("DEPRECATION")
context.resources.getDrawable(R.mipmap.ic_launcher)
}
fun testUnusedValue() {
// The value '"Hello"' assigned to 'var message: String?
// defined in com.example.warningsaserrorscases.test' is never used
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") var message: String? = null
@Suppress("UNUSED_VALUE")
message = "Hello"
}