这是在Kotlin中实例化对象的好方法吗?

时间:2019-10-28 03:14:24

标签: android kotlin

我总是在CameraX项目中找到与代码A一样的代码。它使用类中import React, { useState } from "react" import { Carousel } from "react-responsive-carousel" import { Typography, Dialog, DialogTitle, DialogContent, DialogActions, Button, } from "@material-ui/core" import { makeStyles } from "@material-ui/styles" import { Link } from "gatsby" const useStyles = makeStyles(theme => { return { container: { height: "100%", width: "70%", [theme.breakpoints.up("lg")]: { width: "70%", }, [theme.breakpoints.down("md")]: { width: "90%", }, }, carrusel: { height: "100%", width: "100%", maxWidth: "400px", }, img: { width: "100%", height: "100%", }, text: { color: "#000", padding: "25px", backgroundColor: "#fff", maxWidth: "400px", textTransform: "uppercase", textAlign: "center", }, modal: { width: "95vw", maxHeight: "90vh", display: "flex", flexDirection: "column", alignItems: "center", }, closeIcon: { height: "75px", width: "75px", }, button: { backgroundColor: "#ed1c24", color: "#fff", textAlign: "center", }, buttonFirst: { marginRight: "15px", }, buttonContainer: { display: "flex", alignItems: "center", justifyContent: "center", marginTop: "25px", marginBottom: "75px", }, link: { textDecoration: "none", color: "#fff", }, } }) export default ({ slides, text, ModalComponent }) => { const classes = useStyles() const [openModal, setOpenModal] = useState(false) const handleClose = () => { setOpenModal(false) console.log(openModal) } const handleOpen = () => { setOpenModal(true) } return ( <div className={classes.container} onClick={handleOpen}> <Carousel className={classes.carrusel} infiniteLoop={true} autoPlay interval={5000} showArrows={true} showThumbs={false} showStatus={false} transitionTime={750} showIndicators={true} > {slides.map(item => ( <img className={classes.img} src={item} /> ))} </Carousel> {text ? ( <Typography noWrap className={classes.text} variant={"h4"}> {text} </Typography> ) : null} <Dialog onRequestClose={handleClose} className={classes.modal} open={openModal} onClose={handleClose} maxWidth={"xl"} fullWidth={true} > <DialogTitle>{text}</DialogTitle> <DialogContent> <ModalComponent slides={slides} /> </DialogContent> <DialogActions className={classes.buttonContainer}> <Button className={`${classes.button} ${classes.buttonFirst}`} onClick={handleClose} > Cerrar </Button> <Button className={classes.button} onClick={handleClose}> <Link className={classes.link} to="#contact-form"> Cotizar </Link> </Button> </DialogActions> </Dialog> </div> ) } 内的对象创建实例。

如果我写相同的代码,我将使用代码B。

用Kotlin实例化类中的对象是一种好方法吗?代码A是否优于代码B?

顺便说一句,我没有想到“每个Fragments类都必须有一个空的构造函数”。您可以考虑使用普通类而不是Fragments类吗?

代码A

companion object

调用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)
            }
        }
    }
}

代码B(已修改)

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)
        }
    }

}

5 个答案:

答案 0 :(得分:4)

如果有Fragment

根据定义默认构造函数的android.googlesource.comFragment类的源代码,我们看到:

  

每个片段都必须有一个   空的构造函数,因此可以在还原其时实例化它   活动的状态。 强烈建议不要将子类   具有其他带有参数的构造函数,因为这些构造函数   重新实例化该片段时不会调用它;   调用者可以使用setArguments提供参数   然后由片段使用getArguments检索。

     

应用程序通常不应实现构造函数。更喜欢   onAttach(Context)代替。这是应用程序代码可以在第一个地方运行的地方   准备使用片段-片段实际上与之关联的点   它的上下文。某些应用程序可能还希望实现onInflate来检索   来自布局资源的属性,尽管请注意,这是在附加片段时发生的。


出于上述原因,在Fragment中添加非默认构造函数禁止 !!!

之后,使用setArgumentsgetArguments方法是避免添加额外的构造函数的另一种方法。有问题的代码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()

要创建对象时,无需将构造函数调用包装在另一个函数中,除非必须根据程序的性质更改对象的性质。因为这样做会导致增加一个额外的函数调用来创建一个没有任何优势的对象的负担。

在根据程序性质更改对象的情况下,我们必须获得创新设计模式的帮助,以提供更通用,更灵活的方法(例如:工厂方法,抽象工厂, 模式)。


结论:

  1. 在处理 Fragment 类中的对象创建时,应使用代码A 样式。 (由于android.googlesource.com中所述的原因)

  2. 在处理来自Fragment 类的对象时,最好使用代码B 样式。 (因为避免多余的函数调用没有好处)


答案 1 :(得分:0)

否,效率低下。代码B要求您实例化片段的一次性副本,这样您就可以调用工厂方法来创建所需的实际片段。

答案 2 :(得分:0)

  

使用Kotlin在类中实例化对象是一种好方法吗?

不是。另外,这与Kotlin无关,它是与语言无关的pattern

如果以您的方式(B)初始化Fragment,则将遇到三个问题:

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()
    }
}

答案 3 :(得分:0)

在kotlin中,object属性默认表示一个集合Singleton变量和方法。

object Singleton { // or whatever you name it
   //singleton members
}

它是惰性的并且是线程安全的,它在第一次调用时就进行初始化,就像Java的静态初始化程序一样。

您可以在顶级或类或其他对象内部声明一个对象。

因此,您可以使用伴随关键字标记类内的对象声明:

class UIFragmentPhoto {
    companion object Factory {
        fun create(): UIFragmentPhoto = UIFragmentPhoto()
    }
}

只需使用类名作为限定符即可调用companion object的成员:

val instance = UIFragmentPhoto.create()

如果仅使用object而不使用companion,则必须这样做:

val instance = UIFragmentPhoto.Factory.create()

据我所知, companion 表示该对象与外部类一起使用。 因此,是的,您显然可以将在代码A中创建的instance用于代码B。 如有任何混淆,请通知我。我会尽力帮助您。谢谢

答案 4 :(得分:0)

片段 必须 具有公共的无参数构造函数,并且 应该 除了该构造函数外,没有其他构造函数;相反,该片段需要的所有参数都应放在其arguments捆绑包中。这是因为当Android需要动态构造片段时,它可以通过调用空的构造函数来实现。

这样,即使您想使用代码B(通常也不建议这样做),您的默认构造函数也应该是公共的,在这种情况下,您的辅助构造函数仅起到与create方法相同的作用在代码A中。