Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/402.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么类/接口可以';如果类/接口具有val属性或泛型类型的函数,则不能以out作为前缀?_Java_Kotlin_Generics - Fatal编程技术网

Java 为什么类/接口可以';如果类/接口具有val属性或泛型类型的函数,则不能以out作为前缀?

Java 为什么类/接口可以';如果类/接口具有val属性或泛型类型的函数,则不能以out作为前缀?,java,kotlin,generics,Java,Kotlin,Generics,在Kotlin学习泛型时,我在一本书中读到以下内容: // note that type on left side of = is different than the one on right val numberList: MyList<Number> = MyList<Int>() 通常,如果类具有将其用作返回类型的函数,或者如果类具有该类型的val属性,则类或接口泛型类型的前缀可以为out。但是,如果类具有该泛型类型的函数参数或var属性,则不能使用out 我

在Kotlin学习泛型时,我在一本书中读到以下内容:

// note that type on left side of = is different than the one on right

val numberList: MyList<Number> = MyList<Int>()
通常,如果类具有将其用作返回类型的函数,或者如果类具有该类型的val属性,则类或接口泛型类型的前缀可以为out。但是,如果类具有该泛型类型的函数参数或var属性,则不能使用out

我理解规则所说的,但我很乐意(通过示例)理解没有这个规则会有什么(即在声明泛型类/接口时使用out时没有约束),以及为什么它不是“危险的”返回类型可以来自类型T,并且仍然类/接口可以包含outT

无法理解类属性将作为协变变量的问题的示例:

   class Pet{....}
class Dog:Pet{...}

class PetSomething <T : Pet>  
{
    T t;
    public fun petDoSomething(T t)
    {
        ....   // what can be the problem here?
    }
}


class DogSomething
{
    dogDoSomething()
    {
        d : Dog = Dog()
        petDoSomething(d)
        //what is the problem here???
    }
}
类宠物{….}
班犬:宠物{…}
类宠物
{
T;
公共娱乐宠物用品(T)
{
....//这里有什么问题?
}
}
什么东西
{
dogDoSomething()
{
d:Dog=Dog()
petDoSomething(d)
//这里有什么问题???
}
}
此外,本书还显示以下代码:

抽象类E(t:t){val x=t}

尽管泛型类型是构造函数的输入,但代码仍在编译中。它不是违反了规则吗?

问题是:

val x = DogSomething() 
val y: PetSomething<Pet> = x // would be allowed by out
y.petDoSomething(Cat())
val x=DogSomething()
val y:PetSomething=x//将被out允许
y、 petDoSomething(Cat())
请注意,
Dog上的
petDoSomething
东西
只需处理
Dog
s

尽管泛型类型是构造函数的输入,但代码仍在编译中。这不违反规定吗

它不是,因为构造函数不是相关意义上的成员;无法在上面的
y
上调用它。

您引用了:“但是,如果类具有该泛型类型的函数参数或var属性,则不能使用out。”


构造函数不是成员函数或属性,因此不受此规则约束。在构造函数的站点上为参数使用该类型是安全的,因为在构造该类型时,该类型是已知的

考虑以下类别:

abstract class Pet

class Cat: Pet()
class Dog: Pet()

class PetOwner<out T: Pet>(val pet: T)
然后你可以这样做:

val catOwner: PetOwner<out Cat> = PetOwner(Cat())
val petOwner: PetOwner<out Pet> = catOwner
petOwner.pet = Dog()
val cat: Cat = catOwner.pet // ClassCastException!
val-catOwner:PetOwner=PetOwner(Cat())
val petOwner:petOwner=catOwner
petOwner.pet=Dog()
val cat:cat=catOwner.pet//ClassCastException!

类型安全规则阻止这种情况发生。但是这对于
val
构造函数参数是不可能的。在将参数传递给构造函数和拥有一个可以传递的实例之间,没有办法将对象传递给其他变量并向上转换其类型。

首先让我们通过在
类型参数
前面加上
out
关键字
来明确得到什么。考虑下面的<代码>类< /代码>:

class MyList<out T: Number>{
    private val list: MutableList<T> = mutableListOf()
    operator fun get(index: Int) : T = list[index]
}
如果删除out关键字并再次尝试编译,则会出现类型不匹配错误

通过在
类型
参数
前面加上
out
,您基本上是将
类型
声明为
T
生产者,在上面的示例中
MyList
是数字生产者。 这意味着,无论您将
实例化为
Int
Double
Number
的其他子类型,您总是能够从
MyList
中获得一个数字(因为
Number
的每个子类型都是
数字
)。这还允许您执行以下操作:

fun process(list: MyList<Number>) { // do something with every number }
fun main(){
  val ints = MyList<Int>()
  val doubles = MyList<Double>()
  process(ints)     // Int is a Number, go ahead and process them as Numbers
  process(doubles)  // Double is also a Number, no prob here
}
// if you remove out, you can only pass MyList<Number> to process
这就是为什么需要约束,它基本上是为了保证类型安全

至于正在编译的抽象类E(t:t){valx=t}
,有以下说法

请注意,构造函数参数既不在in中也不在out中 位置。即使类型参数声明为out,您仍然可以 在构造函数参数中使用它。差异保护阶级 如果您将其作为 更一般的类型:你不能调用潜在的危险 方法。构造函数不是以后可以调用的方法 (在创建实例之后),因此它不可能 危险


你能重新表述你问题的第一部分吗?我不明白。“如果没有这个规则(即在声明泛型类/接口时使用out时没有约束),可能会发生什么情况?”@Tenfour04,我为这个问题添加了一个示例。也许这有助于你理解我的要求。如果不是,我将尝试另一种方法,因为这对我来说有点困难。“构造函数不是成员函数或属性,因此不受此规则的约束。”但是,所述规则实际上并不完整(您也不能有
var-pets:List
。@Tenfour04,感谢您的解释和示例(在这种情况下,示例非常有用)。请您也参考我在帖子中创建的示例,并告诉我是否存在ClassCastException的情况?您的示例与上面的示例没有什么不同,只是它没有使用在构造函数中定义属性的快捷方式。该属性仍在实例化时赋值。如果是,编译器将显示错误你对泛型做了一些不安全的事情,或者如果你做了一个潜在的不安全的手动转换(使用
as
关键字),就会显示一个警告。我上面的例子不会编译,因为你不能将
out
var
属性使用的类型组合。我很抱歉在两周后回复你。但直到今天,我还没有回复“战斗”来理解泛型。所以直到今天我才有足够的(我希望)知识来回应你的帖子。是否有可能创建/找到不适用于列表的示例,
fun process(list: MyList<Number>) { // do something with every number }
fun main(){
  val ints = MyList<Int>()
  val doubles = MyList<Double>()
  process(ints)     // Int is a Number, go ahead and process them as Numbers
  process(doubles)  // Double is also a Number, no prob here
}
// if you remove out, you can only pass MyList<Number> to process
fun add(value: T) { list.add(T) }  // MyList has this function

fun main() {
    val numbers = getMyList()   // numbers can be MyList<Int>, MyList<Double> or something else
    numbers.add(someInt)        // cant store Int, what if its MyList<Double> ( Int != Double) 
    numbers.add(someDouble)     // cant do this, what if its MyList<Int>
}

// We dont know what type of MyList we going to get
fun getMyList(): MyList<Number>(){
  return if(feelingGood) { MyList<Int> () }
         else if(feelingOk> { MyList<Double> () }
         else { MyList<SomeOtherSubType>() }
}