Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
193 views
in Technique[技术] by (71.8m points)

generics - Scala inherited type doesn't satisfy parent type requirement

Have been using Scala for a while, and hoping to get into domain modeling using type constraints. In the following code, I am trying to design a domain. Excuse the cryptic classnames, wanted to focus on the specific problem I am facing at hand.

Here is the code:

import scala.collection.mutable

class ClassTypeTest
{
    trait TSM
    {
        def fit[T <: TSM](): FR[T]
    }

    abstract class FM extends TSM{}

    trait MP[T <: TSM]
    {
        def name: String
    }

    trait FiM[T <: TSM]
    {
        val fittedParams = mutable.HashMap[String, MP[T]]()
    }

    class FR[T <: TSM](fm: FiM[T])


    // Now define classes related to SMA
    //===================================
    abstract class SMAP extends MP[SMA]
    class SMAO(n: String) extends SMAP
    {
        override def name = n
    }
    class SMAM(smao: SMAO) extends FiM[SMA]
    {
        fittedParams(smao.name) = smao
    }

    class SMA extends FM
    {
        override def fit[SMA]() =
        {
            val fim = new SMAM(new SMAO("x"))
            //*******************************
            // Following line shows the error:
            // Error:(40, 25) type mismatch;
            // found   : ClassTypeTest.this.SMAM
            // required: ClassTypeTest.this.FiM[SMA]
            //            new FR[SMA](fim)
            //*******************************************
            new FR[SMA](fim)
        }
    }
}

I am defining SMAM as extends FiM[SMA], so why is the compiler complaining about Type mismatch. Required FiM[SMA], found: SMAM. Have I ill-defined one of the type parameters or constraints?

I am trying to constrain the type of fit method, and the FR object to one of the subclasses of TSM. How do I achieve this?

question from:https://stackoverflow.com/questions/65626449/scala-inherited-type-doesnt-satisfy-parent-type-requirement

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
    trait TSM
    {
        def fit[T <: TSM](): FR[T]
    }

This defines a trait with a method fit that for any type T returns an instance of FR[T]

    class SMA extends TSM 
    {
        override def fit[SMA]() =
        {
            val fim = new SMAM(new SMAO("x"))
            new FR[SMA](fim)
        }
    }

This overrides method fit defined in the trait. SMA in method definition is a type parameter, not a reference to the class name. You can (probably, should) replace it with T, it'll be equivalent, but a bit less confusing:

   override def fit[T <: TSM](): FR[T] = new FR(new SMAM(new SMAO("x")))

This will give you a more descriptive error - something to the effect that you are trying to return FR[SMA] where FR[T] is expected.

The bottom line is you cannot make a method in subclass less "generic" than it is declared in a superclass. To better understand why that is the case, consider this:

   abstract class Foo extends TSM 

   val sma: TSM = new SMA()
   val foo: FR[Foo] = sma.fit[Foo]()

This should work: sma is an instance of TSM, and TSM.fit is supposed to work for any type parameter which is a subclass of TSM, which Foo clearly is. But your SMA.fit as written can only return FR[SMA], so the snippet above would not work if it was allowed.

How to fix this? Well, that depends on what you actually want. One possibility is to parametrize the trait itself rather than the method:

    trait TSM[T <: TSM[_]] {
       def fit(): FR[T] 
    }

    class SMA extends TSM[SMA]
    {
        override def fit() = new FR(new SMAM(new SMAO("x")))        
    }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...