4

By this answer https://stackoverflow.com/a/1759565/11217621, I know in Java it is possible to do something like

public class MyClass<S, T> {
   public        void foo(Set<S> s, Set<T> t); //same type params as on class
   public <U, V> void bar(Set<U> s, Set<V> t); //type params independent of class
}

where <U, V> for the bar method are independent from the class parametric type.

I have a simple data class in Java like

public class DataPoint<T> {

    public long timeStampMs;
    public T value;

    public <R> DataPoint<R> withNewValue(R newValue){
        return new DataPoint(this.timeStampMs, newValue);
    }

    public KeyedDataPoint withKey(String key){
        return new KeyedDataPoint(key, this.timeStampMs, this.value);
    }
}

...in such way that from an original DataPoint<Long>, I apply some mapping function to the value field, and the value turns into a Double. By using the method withNewValue there is no problem to instantiate a new DataPoint<Double>

public DataPoint<Double> map(DataPoint<Long> dataPoint) {
    double phase = (double) currentStep / numSteps;
    return dataPoint.withNewValue(phase);
}

I need to migrate this to Scala, and I can't figure out how to do it. I'm trying to do something like:

class DataPoint[T1] (val timeStampMs: Long, val value: T1) {
    def withNewValue(value: T2): DataPoint[T2] = new DataPoint[T2](this.timeStampMs, value)
    def withKey(key: String): KeyedDataPoint[T1] = new KeyedDataPoint(key, this.timeStampMs, this.value)
}

...which does not compile. Also tried several combinations following the official documentation about Scala covariance and contravariance but I'm still in my first steps with Scala.

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92

1 Answers1

3

You're only missing the type parameter [T2] on withNewValue:

class DataPoint[T1] (val timeStampMs: Long, val value: T1) {
    def withNewValue[T2](value: T2): DataPoint[T2] = new DataPoint[T2](this.timeStampMs, value)
    def withKey(key: String): KeyedDataPoint[T1] = new KeyedDataPoint(key, this.timeStampMs, this.value)
}

Compiles just fine.

Markus Appel
  • 3,138
  • 1
  • 17
  • 46
  • Thanks Markus, I could not find anywhere on the Internet how to do it :) By the way, do you know if there is any specific term to refer this, I could not find anything out there by looking up just "type variance" or "generics" – diegoruizbarbero Mar 18 '19 at 17:32
  • 1
    @diegoruizbarbero "type parameters"? If the list with the type parameters is not there, then the type parameter `T2` is unavailable. – Andrey Tyukin Mar 18 '19 at 17:44
  • 1
    @diegoruizbarbero Yeah, "type parameters" is the term you are looking for. What's `` in Java is `[T]` in Scala - with a slightly different syntax. – Markus Appel Mar 18 '19 at 17:51
  • I got the syntax for `[T]` as in `` ...but what was confusing me was the: `public DataPoint withNewValue(R newValue)`. If `DataPoint` is the new (second) return type of the function, what is the first `` in ` DataPoint`? is there a term for that? – diegoruizbarbero Mar 18 '19 at 21:30
  • @diegoruizbarbero Yes, that's the type parameter of the function `withNewValue`. You're using that type parameter in the return type, but you are not declaring it. – Markus Appel Mar 19 '19 at 07:12
  • 1
    @diegoruizbarbero To clarify, these two are completely disjunct, and there is only one return type: `DataPoint`. The `` declares that the function contains a parametric type (which is USED in the return type in this case). That's why imo the syntax of Scala makes more sense, because the type parameter is defined similary to normal parameters instead of "somewhere in front of the method" like in Java. Think: `def myFunc[... type parameters...](... value parameters...)` – Markus Appel Mar 19 '19 at 07:57
  • that's the clarification I needed: it is a new parameter type for THAT function. Essentially all the generics scenarios I had been presented to date were solvable by a combination of `super`/`extends` and wildcards, I wasn't aware you could have other different parameter types in the same class after you have parametrized the class definition. It totally makes sense to define the parameter of the function in the same position as when you parametrize a collection or a class. I finally got it Much appreciated @Andrey and @Markus – diegoruizbarbero Mar 19 '19 at 08:32
  • @diegoruizbarbero If the above answer solved your problem, then please take a second to mark the question as solved by [accepting an answer](https://stackoverflow.com/help/someone-answers). – Andrey Tyukin Mar 19 '19 at 13:13