简体   繁体   中英

Create a generic class (with wildcard types) and multiple constructors

I'm quite new to Scala (2.8) and here's something that I'm struggling to express in Scala:

I need to define a class that (due to interoperability with a Java library) implements Comparable; its generic type has to be Comparable with itself or a superclass

I also need to have a no-args constructor along with another that makes use of the generic parameter

I wrote a simple equivalent of what I'm trying to get in Java:

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

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

I can import without any problem this class in the scala REPL and instantiate it.

This is what I'm writing in Scala to try to accomplish the same thing:

import java.lang.Comparable

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

I tried both by using the no-args constructor as the default one, or using the one with the T argument: in both cases I get error: could not find implicit value for parameter ev: =:=[T,Tb] at line 5

Afaik, =:= is imported by default from scala.Predef (and in fact, this code works fine with only one constructor)

Not entirely certain what you're trying to do, but you can do it like this:

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

You are over-complicating the issue -- though it would be nice if Java had declaration-site variance, which would make all of this moot. Anyway, here's the equivalent code:

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

Granted that this does not use raw type, and has two type parameters instead of one. Then again, there's the question of what you are actually trying to accomplish with that declaration. I wonder if what you actually want isn't this:

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

I tell Scala to ignore variance above at my peril, because I assume Comparable can, indeed, be contra-variant. The following code indicates that's indeed the case:

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

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

This still isn't the same as the Java code I posted, but it seems to be close enough and cleaner than the other solutions:

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

Here's a bunch of miscellaneous links I read since when I got stuck with this problem, that may help someone else with a similar problem:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM