我想要是什么:
interface base {
abstract static fun foo()
}
class impl : base {
override static fun foo()
}
通常,Kotlin使用伴随对象而不是静态函数来解决问题。但是接口无法定义对具有功能的伴随对象的要求。那我该怎么做呢?使用此代码的代码看起来像
fun <T : base> bar() {
T.foo()
}
还有其他方法可以得到这种行为吗?也就是说,我可以执行T
的派生函数而无需知道具体类型,并且不假设派生具有默认构造函数吗?
修改
通过使用可以在要使用的类的伴随对象上设置的类型的值参数,我能够做到这一点。我想要将此技术用于什么的说明性示例。
import kotlin.reflect.full.*
interface DynamicBuilder {
fun build(sides: Int): Shape?
}
interface Shape {
companion object : DynamicBuilder {
override fun build(sides: Int) = null
}
}
abstract class Shape2D : Shape {
companion object : DynamicBuilder {
override fun build(sides: Int) = if(sides > 0) Square() else Circle()
}
}
abstract class Shape3D : Shape {
companion object : DynamicBuilder {
override fun build(sides: Int) = if(sides > 0) Cube() else Sphere()
}
}
class Square : Shape2D()
class Circle : Shape2D()
class Sphere : Shape3D()
class Cube : Shape3D()
fun Build(sides: Int, builder: DynamicBuilder): Shape? {
return builder.build(sides)
}
inline fun <reified T : Shape> Build(sides: Int): Shape? {
return Build(sides, T::class.companionObjectInstance as DynamicBuilder)
}
fun main() {
println(Build(0, Shape2D))
println(Build(4, Shape2D))
println(Build<Shape3D>(0))
println(Build<Shape3D>(6))
}
目标是我可以创建一个Shape
的新类,并具有与其如何构建包含在该文件中的具体对象有关的所有逻辑,而不是具有某些整体式共享switch语句。
答案 0 :(得分:2)
接口可以定义具有功能的 some 对象的要求,即使您不能强迫它成为伴生对象,也可以建议它。
interface BaseCompanion {
fun foo(): Unit
}
interface Base {
companion object : BaseCompanion {
fun foo() { println("in Base") }
}
fun companion(): BaseCompanion = Base
}
interface Derived : Base {
companion object : BaseCompanion {
fun foo() { println("in Derived") }
}
override fun companion() = Derived
}
// value parameter, not type parameter
fun bar(companion: BaseCompanion) {
companion.foo()
}
bar(Base)
bar(Derived)
在这种情况下,实际上并未使用companion()
函数,它是用于您要从Base
实例访问随播广告的情况:
fun baz(x: Base) {
x.companion().foo()
}
另一个(不安全的)选项是使用反射来定义companion()
。
fun companion() = this::class.companionObjectInstance as BaseCompanion
加:无需在Derived
中显式覆盖它;缺点:1.如果忘记创建同伴或扩展BaseCompanion
,则会在运行时崩溃; 2.比非反射定义要慢。
答案 1 :(得分:0)
TL; TR :
科特林没有静态方法。即使有它们,它们也不会被覆盖,因为它们不是Java语言。伴随对象也是如此。最终Kotlin代码被编译为Java字节码,因此Kotlin也无法实现Java中不可能实现的功能。
编辑:
看看编译器对此有什么看法很有趣。请考虑以下代码段:
open class Base {
companion object {
fun test() {}
}
}
inline fun <reified T : Base> staticCall() {
T.test() // <-- ERROR
}
错误消息:
类型参数“ T”不能具有或继承伴随对象,因此它不能位于点的左侧
答案 2 :(得分:0)
根据您更新的问题,通常可以使用工厂模式实现所需的目标。另外,您也可以使用依赖注入。有很多选项不使用反射。
为什么不使用反射?
有here和here的几个原因,如果您使用Google可以找到更多的原因。通常,反射是为特定目的而创建的,以发现编译时未知的类的功能。请勿将其用于此目的,因为您的实现要求您知道该类,以便将其作为经过修饰的泛型参数进行传递。如果确实需要发现在编译时不知道的类,则可以使用依赖注入。
您的版本最简单的解决方案是工厂模式:
interface Shape
class Square : Shape
class Circle : Shape
class Sphere : Shape
class Cube : Shape
object ShapeFactory {
fun build2DShape(sides: Int): Shape {
if(sides > 0) Square() else Circle()
}
fun build3DShape(sides: Int): Shape {
if(sides > 0) Cube() else Sphere()
}
}
fun main() {
println(ShapeFactory.build2DShape(0))
println(ShapeFactory.build3DShape(0))
}
简而言之,Build<Shape3D>(0)
被ShapeFactory.build3DShape(0)
代替。呼叫者仍然必须知道3DShapes及其位置。唯一改变的是您不需要反射。
这要求调用函数的人员知道2D和3D形状的存在。与实施反射相同。通过这种方式,您可以拥有所有逻辑以及如何在与形状相同的文件中创建形状。如果愿意,您甚至可以在工厂的形状对象中调用某些函数。您的工厂知道这些子类的存在。但是,由于您可以将工厂和子类放在同一个文件中,因此不会将逻辑拆分到其他地方。
如果要将决定是2D形状还是3D形状委托给子类,则可以执行以下操作:
interface Shape
class Square : Shape
class Circle : Shape
class Sphere : Shape
class Cube : Shape
object ShapeFactory {
fun build2DShape(sides: Int): Shape {
return if(sides > 0) Square() else Circle()
}
fun build3DShape(sides: Int): Shape {
return if(sides > 0) Cube() else Sphere()
}
}
fun getBuilder(dimensions: Int): (sides: Int) -> Shape {
if (dimensions == 2)
return ShapeFactory::build2DShape
else
return ShapeFactory::build3DShape
}
fun main() {
print (getBuilder(2)(3))
}