Kotlin类型安全类型别名

Kotlin类型安全类型别名,kotlin,type-alias,Kotlin,Type Alias,我在Kotlin代码中经常使用TypeAlias,但我想知道是否可以对它们强制执行类型安全性 typealias Latitude = Double typealias Longitude = Double fun someFun(lat: Latitude, lon: Longitude) {...} val lat: Latitude = 12.34 val lon: Longitude = 56.78 someFun(lon, lat) // parameters are in a w

我在Kotlin代码中经常使用TypeAlias,但我想知道是否可以对它们强制执行类型安全性

typealias Latitude = Double
typealias Longitude = Double

fun someFun(lat: Latitude, lon: Longitude) {...}

val lat: Latitude = 12.34
val lon: Longitude = 56.78
someFun(lon, lat) // parameters are in a wrong order, but the code compiles fine
如果我能以某种方式防止类型别名之间的隐式转换,帮助避免此类问题,那就太好了

当然,存在一个问题,即对基本类型的操作对于TypeAlias不可用,但可以通过扩展函数(或强制转换)来解决

我不想使用数据类,只保留一个字段,因为这似乎有点过分,特别是对于基本类型(或者我错了,它们会被优化掉?)


所以问题是:我是否可以以某种方式强制类型别名的类型安全性

不幸的是,这对于typealias是不可能的。kotlin的参考资料说:

类型别名不会引入新类型。它们相当于 相应的底层类型。添加
typealias谓词时
并在代码中使用
谓词
,Kotlin编译器始终会展开 将其转换为
(Int)->布尔值
。因此,您可以传递您类型的变量 无论何时需要通用功能类型,反之亦然:

typealias Predicate<T> = (T) -> Boolean

fun foo(p: Predicate<Int>) = p(42)

fun main(args: Array<String>) {
    val f: (Int) -> Boolean = { it > 0 }
    println(foo(f)) // prints "true"

    val p: Predicate<Int> = { it > 0 }
    println(listOf(1, -2).filter(p)) // prints "[1]"
}
typealias谓词=(T)->Boolean
fun-foo(p:谓词)=p(42)
趣味主线(args:Array){
val f:(Int)->Boolean={it>0}
println(foo(f))//打印“true”
val p:谓词={it>0}
println(listOf(1,-2).filter(p))//打印“[1]”
}
见科特林

tl;您必须使用(数据)类

正如名称typealias所暗示的,typealias只是一个别名,而不是一个新类型。在您的示例中,
纬度
经度
整数
,与它们的名称无关。要使其类型安全,您必须声明一个类型。理论上,您可以从
Int
继承新类型。因为
Int
是最后一个类,所以这是不可能的。因此需要创建一个新类

Kotlin 1.3的更新 自Kotlin 1.3起,内联类已经可用,并且当前标记为实验类

原始答案
不幸的是,您目前无法避免这种情况。有一个正在开发的特性-,它将解决运行时开销问题,同时强制执行编译时类型安全。它看起来非常类似于,如果您有大量数据,它非常方便,而普通的case类将是一种开销。

通过将
纬度
经度
定义为
Double
的别名,可以将其视为可传递的别名,即,您将
纬度
定义为
经度
的别名,反之亦然。现在,所有三种类型名称都可以互换使用:

val d: Double = 5.0
val x: Latitude = d
val y: Longitude = x
作为替代方案,您可以简单地使用参数名称来明确传递的内容:

fun someFun(latitude: Double, longitude: Double) {
}

fun main(args: Array<String>) {
    val lat = 12.34
    val lon = 56.78
    someFun(latitude = lon, longitude = lat)
}
fun someFun(纬度:双精度,经度:双精度){
}
趣味主线(args:Array){
val lat=12.34
瓦隆=56.78
someFun(纬度=经度,经度=纬度)
}

我最近也遇到了类似的情况。内联类不是解决方案,因为它迫使我使用属性包装器

希望对我来说,我已经设法通过继承授权解决了我的问题

class ListWrapper(list: List<Double>): List<Double> by list

但这要求我们用反射“打开”父类,因为“@Suppress”(“FINAL_SUPERTYPE”)不起作用


不幸的是,对于原语还有另一个问题,因为它们只提供了空的私有构造函数,并且用一些未记录的魔法进行了初始化。

我已经读了不止一次了。问题是为什么会这样做,以及如何绕过这个限制。我补充了一个简短的解释,为什么这是不可能的。希望它能帮助您:)Int不是一个最终类,它是一个基本类。智力?是最终类。在kotlin
Int
中,是最终类。它扩展
数字
并实现
可比
。它在
kotlin.Primitives.kt中声明,这也是一个可能的解决方案。您可以创建extand
Number
的类。但是我现在不知道这些类的互操作性和兼容性是什么。一个很好的文档#4将是一个致命的特性:)不过,我不明白为什么内联类应该提供运行时开销。另外,我的问题也可以通过smth解决,比如
strict typealias Longitude=Double
,其中strict意味着只允许对类型执行显式强制转换。内联类没有任何运行时开销,因为在编译器完成AST上的类型检查后,值从不装箱/取消装箱,这些值保留在底层类型中,因此在您的情况下,当VM执行代码时,这两个值仍然很长。我所说的运行时开销是Scala中的case类,当它被用作单个值的类型安全包装器时(这是Scala的SIP-15的主要原因)。目前唯一的方法是创建显式类型<代码>类纬度:双
。还是我在这里遗漏了什么?编译器随后将强制执行类型化,但在大多数情况下,您仍然可以将其视为双精度。@Mikezx6r是的,您遗漏了几件事:1)您不能从基元类型继承,2)您不能从最终类继承3)此方法有运行时开销不要怪我,这不符合主题,请关注“智能”将我的问题标记为此问题副本的人
class ListListWrapper(list: ListWrapper): ListWrapper by list