扩展方法和扩展属性是不好的做法吗?
所以如果扩展方法和扩展属性真的是静态的方法和属性。 静态方法和属性和方法不是线程安全的,因此应该避免使用扩展方法和扩展属性。
我们只是被欺骗,因为我们编写的代码将显示为漂亮或干净,但性能方面则不然。
这是真的?
这取决于你如何写扩展功能/属性。 如果他们不编辑或访问共享状态,即属性和功能是明确的功能:这是绝对不错的做法。
例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}" }
这个函数改变MutablePerson
对象的speech
属性,赋值操作不是原子的。 如果从不同的线程对一个对象调用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
所以扩展函数和扩展属性不是不好的练习,它们就像类中的属性和方法一样:根据你写的线程是否安全。
静态方法就像实例方法一样拥有自己的栈。 因此,静态方法中的临时变量就像实例方法一样在栈上。 交给静态方法的参数在访问共享状态时可能会遇到线程问题,但这与实例方法的情况完全相同。
考虑Java中使用静态方法的大量的Util类作为Java没有扩展功能的解决方法。 在多线程方面没有什么问题。
另外在C#中,扩展方法是幕后的静态方法,并没有造成任何伤害,请参阅如何在内部实现扩展方法
正如你所说,扩展功能是静态解决的。 所以如果你开始使用扩展函数作为实用工具类,那么这是一个不好的做法。
在Java中,Utils类通常是不好的做法,不仅因为线程安全,而且因为它们可能是糟糕设计的代码味道,因为它们很难测试。
静态方法的主要问题是它们不能被模拟(至少与Mockito),所以你会提出无法测试你的代码。
但是,如果你使用扩展函数来处理那些不需要测试的小的,孤立的任务,那么这根本就不是一个坏习惯(就像Toasts,Logs的helper一样)