我总是在CameraX项目中找到代码,就像代码A一样。它使用类内的companion object
中的对象创建实例。
如果我写同样的代码,我将使用代码B。
用Kotlin实例化类中的对象是一种很好的方法吗?A代码比B代码好吗?
顺便说一下,我不认为“每个片段类都必须有一个空的构造函数”。请考虑一个普通的类而不是片段类,好吗?
码A
class UIFragmentPhoto internal constructor() : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?) = ImageView(context)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val args = arguments ?: return
val resource = args.getString(FILE_NAME_KEY)?.let { File(it) } ?: R.drawable.ic_photo
Glide.with(view).load(resource).into(view as ImageView)
}
companion object {
private const val FILE_NAME_KEY = "file_name"
fun create(image: File) = UIFragmentPhoto().apply {
arguments = Bundle().apply {
putString(FILE_NAME_KEY, image.absolutePath)
}
}
}
}
调用A
override fun getItem(position: Int): Fragment = UIFragmentPhoto.create(mediaList[position])
码B(修改)
class UIFragmentPhoto internal constructor() : Fragment() {
val FILE_NAME_KEY = "file_name"
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?) = ImageView(context)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val args = arguments ?: return
val resource = args.getString(FILE_NAME_KEY)?.let { File(it) } ?: R.drawable.ic_photo
Glide.with(view).load(resource).into(view as ImageView)
}
constructor(image: File):this(){
arguments = Bundle().apply {
putString(FILE_NAME_KEY, image.absolutePath)
}
}
}
调用B(修改)
override fun getItem(position: Int): Fragment = UIFragmentPhoto(mediaList[position])
发布于 2019-11-01 01:56:22
在Fragment
的情况下:
根据定义默认构造函数的Fragment
类在android.googlesource.com上的源代码,我们看到:
每个片段都必须有一个空的构造函数,因此可以在恢复其活动的状态时实例化它。强烈建议子类没有带有参数的其他构造函数,因为当片段被重新实例化时不会调用这些构造函数;相反,参数可以由调用方提供setArguments,然后由带有getArguments.Applications的片段检索,通常不应该实现构造函数。而更喜欢onAttach(上下文)。这是应用程序代码在准备使用片段的地方运行的第一个地方--片段实际上与其上下文相关联的位置。一些应用程序也可能希望实现onInflate来从布局资源中检索属性,但请注意,这是在附加片段时发生的。
鉴于上述原因,在非默认构造函数中添加Fragment
是禁止的!
之后,使用setArguments
和getArguments
方法是避免添加额外构造函数的另一种方法。有问题的代码B,同时使用这两种方法。您应该使用其中之一,比如代码A。(因为当您将参数传递给构造函数时,可以在类中访问它。所以不需要[set/get]Arguments
模式)
但是,如果我们想不使用参数重写代码B (免责声明:我强调这种方法不是真的),我们可以这样做:
class UIFragmentPhoto internal constructor(private val image: File?) : Fragment() {
constructor() : this(null)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?) = ImageView(context)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val resource = image ?: R.drawable.ic_photo
Glide.with(view).load(resource).into(view as ImageView)
}
}
一般而言:
我们都知道,对象是通过调用Java/Kotlin中类的构造函数方法创建的。例如:
val obj = MyClass()
当您想要创建一个对象时,不需要将构造函数调用包装在另一个函数中,除非必须根据程序的性质更改对象的性质。因为它会导致额外的函数调用来创建一个没有任何优势的对象。
在根据程序性质变化对象的情况下,必须借助创造性的设计模式,提供更通用、更灵活的方法(如:工厂方法、抽象工厂模式等)。
结论:
Fragment
类创建对象时,应该使用代码--样式。(因为android.googlesource.com描述的原因)Fragment
类创建对象时,最好使用代码B**样式。(因为避免了没有优势的额外函数调用)发布于 2019-10-27 19:26:27
不,这是低效的。代码B要求实例化片段的丢弃副本,这样就可以调用工厂方法来创建所需的实际片段。
发布于 2019-10-27 19:46:36
是用Kotlin在类中实例对象的好方法吗?
不怎么有意思。而且,这与Kotlin无关,它是一个语言不可知论的https://www.google.com/search?q=static%20factory%20method。
如果以(B)方式初始化片段,则有三个问题:
1)很容易忘记在您的实例中调用“创建”:
override fun getItem(position: Int): Fragment = UIFragmentPhoto() // Oops - bug
2)如果将create
作为实例方法,则可以在现有片段上反复调用它:
fun someFunction() {
UIFragmentPhoto fragment = getExistingFragment()
fragment.create() // Oops - just overwrote the fragment state
}
3)拥有一个“创建”已经创建的实例的方法只是让人困惑,也是初始化片段的一种非标准方法。这种静态/伴生方式的要点是,您有一个函数,它的任务是创建和初始化对象。
工厂方法还为您提供了在返回对象之前执行错误处理/验证的灵活性,并使您有机会创建一个完全不同的对象类型,该对象类型扩展/实现返回类型(如果您选择的话):
companion object {
fun create(arg: Int): UIFragmentPhoto {
if (arg == 0) throw IllegalStateException("Wasn't expecting 0!")
if (arg == 1) return FragmentThatExtendsUIFragmentPhoto()
if (arg == 2) return UIFragmentPhoto()
}
}
https://stackoverflow.com/questions/58585384
复制