【问题标题】:Create a generic class (with wildcard types) and multiple constructors创建一个泛型类(带有通配符类型)和多个构造函数
【发布时间】:2011-07-17 06:40:21
【问题描述】:

我对 Scala (2.8) 很陌生,这是我在 Scala 中难以表达的东西:

我需要定义一个实现 Comparable 的类(由于与 Java 库的互操作性);它的泛型类型必须与自身或超类可比较

我还需要一个无参数构造函数以及另一个使用泛型参数的构造函数

我写了一个简单的等价物,就像我想用 Java 得到的一样:

public class MyComparable<T extends Comparable<? super T>>{
    public MyComparable() {}

    public MyComparable(T a){
        System.out.println(a);
    }
}

我可以毫无问题地在 scala REPL 中导入这个类并实例化它。

这是我在 Scala 中编写的内容,目的是尝试完成同样的事情:

import java.lang.Comparable

class MyComparable[T <: Comparable[_>:Tb],Tb]()(implicit ev: T=:=Tb) {
    def this(a: T) = {
        this()
        println(a)
    }
}

我尝试使用无参数构造函数作为默认构造函数,或者使用带有 T 参数的构造函数:在这两种情况下,我都在第 5 行得到error: could not find implicit value for parameter ev: =:=[T,Tb]

Afaik,=:= 默认是从 scala.Predef 导入的(事实上,这段代码只需要一个构造函数就可以正常工作)

【问题讨论】:

  • 您最初的问题是什么? class MyComparable[T &lt;: Ordered[_&gt;:T]] 被拒绝了?您的 Scala 代码在概念上与您上面显示的示例 Java 代码没有太大关系。
  • 我最初的问题涉及到 2 个像这样的泛型类,带有访问器/突变器,是泛型参数的 2 倍……总体而言,它更加笨重和凌乱,我可以将它粘贴到其他地方,但我已将我的问题减少到这几行,我认为使用我的原始代码不会给这个问题添加任何内容
  • 我不知道你所说的“拒绝”是什么意思,但显然你不能那样写签名,否则你会得到一个循环引用。我不明白您为什么说 Scala 代码在概念上与 Java 代码不同...您愿意详细说明差异吗?
  • (由于 5 分钟的限制,添加而不是编辑)@soc 我的意思是:两者都有一个泛型参数,该参数必须与其相同的类型或超类型具有可比性,两者都有一个空的构造函数,两者有一个带有泛型参数 TI 的构造函数实际上在我的 Scala 代码中缺少的 Java 代码中留下了 System.out.println(a),这是唯一的区别 afaik,现在我已经修复了它

标签: java scala


【解决方案1】:

不完全确定你想要做什么,但你可以这样做:

import java.lang.Comparable

class MyComparable[T <: Comparable[_>:Tb],Tb]()(implicit ev: T=:=Tb) {
  def this(a: T)(implicit ev: T=:=Tb) = {
    this()
    println(a)
  }
}

【讨论】:

  • 谢谢!我仍然很不习惯 Scala 的构造函数和多个参数列表,所以我之前没有想过将隐式参数复制到另一个构造函数上(你知道……implicit 欺骗了我认为它不是需要)仍然:Java 等价物更方便:我可以像这样实例化 Scala MyComparable:new MyComparable[String,String]()new MyComparable[String,String]("dummy")
  • 而 Java MyComparable 可以像这样使用:new MyComparable()new MyComparable("dummy")new MyComparable[String]()new MyComparable[String]("dummy") 因此,现在我将投票赞成答案并等待看看是否有更好的解决方案(这是我在 SO 上的第一个问题,也许说明这一点是多余的)编辑:显然我不能投票,直到我拥有 15 个声誉:/
  • 我接受了你的回答,原因如下: - 我不喜欢接受我自己的回答 - 你的回答最接近我原来的方法 - 我真的需要 +2 代表来添加额外的链接我的回答(呃:我讨厌这个网站)
【解决方案2】:

您将问题过度复杂化了——尽管 如果 Java 有声明站点差异会很好,这将使所有这些都没有实际意义。无论如何,这是等效的代码:

class MyComparable[T <: Comparable[T2], T2 >: T]() {
    def this(a: T) = {
        this()
        println(a)
    }
}

当然,这不使用原始类型,并且有两个类型参数而不是一个。再说一次,问题是您实际上要通过该声明来完成什么。我想知道你真正想要的不是这个:

import scala.annotation.unchecked.uncheckedVariance
class MyComparable[-T <: Comparable[T @uncheckedVariance]]() {
    def this(a: T) = {
        this()
        println(a)
    }
}

我告诉 Scala 忽略上面的方差,因为我假设 Comparable 确实可以是逆变的。以下代码表明确实如此:

scala> trait Cp[-T] {
     |   def compareTo(other: T): Int
     | }
defined trait Cp

scala> class MyComparable[-T <: Cp[T]] {
     | }
defined class MyComparable

【讨论】:

  • 谢谢,您的第一个建议非常简单,尽管它强制我们明确指定 T2 类型,我们的 T 是其子类型。第二个建议很有趣,但我在我的情况下尝试过,确实没有帮助......我思考了很多,我认为在这里探索逆变没有帮助:问题在于通过的类型给构造函数;只有在从类中获取该类型或将对象重新分配给其他子/超类时,具有逆变参数才有帮助
  • 实际上,在发布这个问题之前,我什至不知道 scala 中协变和逆变的含义,但是由于我有使用普通旧 java 不变量的负担,我的选择已经相当有限。您的第三个示例很简洁,并且(与 sn-p #2 一起)促使我了解方差在 scala 中是如何工作的,但在这种情况下它仍然无法使用:我的类必须能够接受无法实现的现有 java 类型一个 Cp scala 特征(再想一想,我可以将所有需要的类子类化并为每个类混合这个特征,但这样看起来很脏)
【解决方案3】:

这仍然与我发布的 Java 代码不同,但它似乎比其他解决方案足够接近和干净:

import java.lang.Comparable
class MyComparable[T <: Comparable[T]]() {
    def this(a: Comparable[_ >: T]) = {
        this()
        println(a)
    }
}

自从我遇到这个问题以来,我阅读了一堆杂项链接,这可能会帮助遇到类似问题的其他人:

When is @uncheckedVariance needed in Scala, and why is it used in GenericTraversableTemplate?

to understand what does =:= really do

http://programming-scala.labs.oreilly.com/ch12.html#VarianceUnderInheritance

http://www.scala-lang.org/node/129

Type erasure and other low-level details of the Java generics

http://www.scala-lang.org/node/124

http://scalada.blogspot.com/2008/01/existential-types.html

http://lamp.epfl.ch/~emir/bqbase/2007/06/13/existentials.html

【讨论】:

  • 非常好!我也会选择这个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-11
  • 1970-01-01
  • 2010-10-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多