因此,如果扩展方法和扩展属性确实是静态方法和属性。静态方法,属性和方法不是线程安全的,因此应该避免扩展方法和扩展属性不好。
我们只是被欺骗去做那些因为我们编写的代码看起来很漂亮或干净,但性能方面则不然。
这是真的吗?
答案 0 :(得分:9)
这取决于你如何编写扩展功能/属性。如果他们不编辑或访问共享状态,即如果属性和功能是明确的功能:绝对不是不好的做法。
示例1:
fun String.countSpaces(): Int {
return this.count { c -> c == ' ' }
}
此函数在多线程环境中完美运行,因为String
是不可变的。
示例2:
data class MutablePerson(val name: String, var speech: String)
fun MutablePerson.count(nextNumber: Int) {
this.speech = "${this.speech} ${nextNumber}"
}
此函数改变speech
对象的MutablePerson
属性,并且赋值操作不是原子的。如果将在来自不同线程的一个对象上调用count
- 可能存在不一致状态。
示例:
fun main(args: Array<String>) {
val person = MutablePerson("Ruslan", "I'm starting count from 0 to 10:")
(1..10).forEach { it ->
Thread({
person.count(it)
println(person.speech)
}).start()
}
Thread.sleep(1000)
println(person.speech)
}
可能的输出:
I'm starting count from 0 to 10: 1
I'm starting count from 0 to 10: 1 3
I'm starting count from 0 to 10: 1 3 4
I'm starting count from 0 to 10: 1 3 4 2
I'm starting count from 0 to 10: 1 3 4 2 5
I'm starting count from 0 to 10: 1 3 4 2 5 8
I'm starting count from 0 to 10: 1 3 4 2 5 6
I'm starting count from 0 to 10: 1 3 4 2 5 6 7
I'm starting count from 0 to 10: 1 3 4 2 5 6 7 9
I'm starting count from 0 to 10: 1 3 4 2 5 6 7 9 10
I'm starting count from 0 to 10: 1 3 4 2 5 6 7 9 10
所以扩展函数和扩展属性也不错,它们就像类中的属性和方法一样:取决于你编写的线程是否安全。
答案 1 :(得分:7)
静态方法只有实例方法才有自己的堆栈。因此静态方法中的临时变量就像例如方法一样在栈上。在访问共享状态时,传递给静态方法的参数可能会遇到线程问题,但这与实例方法的情况完全相同。
将Java中的大量Util类与静态方法一起考虑为Java没有扩展函数的解决方法。关于多线程,没有任何问题。
同样在C#扩展方法背后的场景静态方法并没有造成任何伤害,请参阅How extension methods are implemented internally
答案 2 :(得分:0)
正如您所说,扩展功能是静态解决的。因此,如果您开始使用扩展函数作为实用类的方法,那么这是一种不好的做法。
在Java中,Utils类通常是一种不好的做法,不仅因为线程安全,还因为它们可能是设计糟糕的代码气味,并且因为它们很难测试。
静态方法的主要问题是它们不能被模拟(至少使用Mockito),因此您将无法测试代码。
但是,如果你对不需要测试的小型,孤立的任务使用扩展函数那么它根本就不是一个坏习惯(比如Toasts,Logs的助手......)