Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>As near as I can tell, the concept you are looking for is "covariance". Just because <code>IntValue</code> is a subtype of <code>Value</code> doesn't mean that <code>MyExtractor[IntValue]</code> is a subtype of <code>MyExtractor[Value]</code>. By default, there is no subtyping relation between those two types at all. To create such a relationship, you need to declare <code>MyExtractor</code> to be covariant with respect to it's parameter. Scala lets you declare type parameters to be covariant by adding a "+" before the type parameters declaration. This is called a variance notation. </p> <pre><code>sealed abstract class MyExtractor[+Value] extends Extractor[Data, Value] { } </code></pre> <p>Scala also supports contravariance over type parameters. Contravariance is just like covariance, but reversed, and is expressed with a "-" variance notation on the type parameter. Your <code>Extractor</code> type provides an excellent example of a place where a contravariance notation makes sense.</p> <pre><code>abstract class Extractor[-A,+B] { def extract(d:A):B def stringRepr(d:A):String } </code></pre> <p>This means that if <code>Foo</code> is a subtype of <code>Bar</code>, then <code>Extractor[Bar, Baz]</code> is a subtype of <code>Extractor[Foo, Baz]</code>, which if you think about it makes sense. If something can extract the data you want when passed an instance of a supertype, then by definition it can extract it when passed an instance of a subtype. Conversely, if <code>Foo</code> is a subtype of <code>Bar</code>, then <code>Extractor[Baz, Foo]</code> is a subtype of <code>Extractor[Baz, Bar]</code>. That also makes sense. If you've got an extractor that returns a <code>Foo</code>, you can certainly use it wherever you need an extractor that returns a <code>Bar</code>.</p> <p>There are restrictions on when contravariance and covariance can be declared. For instance, contravariant type parameters can only be used as method arguments, and covariant parameters can only be used as method returns or vals. Neither can be used as vars. It gets more complicated with nested type parameters, but the rules basically boil down to "where it's sensible", and your example meets all of them. </p> <p>Additional side note, all of your abstract classes in your example should probably be declared as traits instead. As long as your abstract classes don't require constructor arguments, declaring them as traits gives you a few more opportunities for reuse.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload