Хобрук: Ваш путь к мастерству в программировании

Унаследованный тип Scala не удовлетворяет требованиям родительского типа

Некоторое время я использовал Scala и надеялся заняться моделированием предметной области с использованием ограничений типа. В следующем коде я пытаюсь создать домен. Простите за загадочные имена классов, хотел сосредоточиться на конкретной проблеме, с которой я столкнулся.

Вот код:

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

Я определяю SMAM как extends FiM[SMA], так почему же компилятор жалуется на Type mismatch. Required FiM[SMA], found: SMAM. Я неправильно определил один из параметров или ограничений типа?

Я пытаюсь ограничить тип метода соответствия и объект FR одним из подклассов TSM. Как мне этого добиться?

08.01.2021

Ответы:


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

Это определяет черту с методом fit, который для любого типа T возвращает экземпляр FR[T]

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

Это переопределяет метод fit, определенный в трейте. SMA в определении метода - это параметр типа, а не ссылка на имя класса. Вы можете (возможно, должны) заменить его на T, это будет эквивалентно, но немного менее запутанно:

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

Это даст вам более описательную ошибку - что-то вроде того, что вы пытаетесь вернуть FR[SMA], где ожидается FR[T].

Суть в том, что вы не можете сделать метод в подклассе менее универсальным, чем он объявлен в суперклассе. Чтобы лучше понять, почему это так, рассмотрим следующее:

   abstract class Foo extends TSM 

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

Это должно работать: sma является экземпляром TSM, а TSM.fit должен работать для любого параметра типа, который является подклассом TSM, что явно Foo. Но ваш SMA.fit в том виде, в каком он написан, может возвращать только FR[SMA], поэтому приведенный выше фрагмент не работал бы, если бы он был разрешен.

Как это исправить? Ну, это зависит от того, чего вы на самом деле хотите. Одна из возможностей - параметризовать сам признак, а не метод:

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

    class SMA extends TSM[SMA]
    {
        override def fit() = new FR(new SMAM(new SMAO("x")))        
    }
08.01.2021
  • Это подход F-границ, верно? Немного читал об этом на основе комментария от @HTNW ранее (tpolecat .github.io / 2015/04/29 / f-bounds.html), и похоже, что есть некоторые подводные камни с точки зрения дыр, но все же очень удобное решение. 08.01.2021
  • @SamikR да, это F-Bound, взгляните на this для других альтернатив и их компромиссов. 08.01.2021

  • 2

    Я определяю SMAM как extends FiM[SMA], так почему компилятор жалуется на Type mismatch. Required FiM[SMA], found: SMAM.

    SMA in def fit[SMA]() = ... in

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

    является параметром типа. Это произвольный тип, а не класс SMA (параметр типа SMA hid class SMA). Здесь вы можете использовать произвольный идентификатор, например T

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

    Так что я предполагаю ошибку

    type mismatch;
     found   : SMAM
     required: FiM[T]
    

    теперь ясно.

    08.01.2021
  • Как вы думаете, OP намеревался T в TSM#fit быть абстрактным типом члена (или просто универсальным параметром класса, ограниченным F)? 08.01.2021
  • @HTNW Думаю, чтобы ответить на этот вопрос, мы должны понимать бизнес-логику OP :) 08.01.2021
  • Привет - спасибо за быстрый ответ. @HTNW: Думаю, я недостаточно понимаю разницу между этими двумя случаями, о которых вы упомянули, но бизнес-требование состояло в том, что я хотел, чтобы объект FR был определен с одним из подклассов TSM. 08.01.2021
  • @DmytroMitin: Думаю, я понимаю, что вы говорите: я ошибочно принял SMA за тип класса, но на самом деле это произвольный тип, который вы показываете, заменяя SMA на T. Как мне продолжить, чтобы добраться до того, что Я хочу добиться того, о чем говорилось выше? 08.01.2021
  • Новые материалы

    Управление DOM для чайников вроде меня
    Одной из первых вещей, которую мы рассмотрели, когда начали изучать Javascript во Flatiron, была модель DOM. Кто он? Чем он занимается? Он больше машина, чем человек? Ну да довольно много. ДОМ..

    Что такое структура данных?
    Структура данных хранит и извлекает данные. Все, что обеспечивает эти две функции, является структурой данных . Период. Вы можете пропустить оставшуюся часть статьи, если ответ..

    мои январские чтения по программированию
    Эрик Эллиот Программирование приложения JavaScript Эл Свейгарт «Автоматизируйте скучные вещи с помощью Python» Прогрессивное веб-приложение Google..

    Создание ассоциаций секвелизации с помощью инструмента командной строки Sequelize
    Sequelize - популярный, простой в использовании инструмент объектно-реляционного сопоставления (ORM) JavaScript, который работает с базами данных SQL. Довольно просто начать новый проект с..

    Искусственный интеллект в юридической отрасли - пример прогнозирования судебных решений с помощью глубокого обучения
    На протяжении всей истории люди полагались на суды, присяжных, королей и королев в отправлении правосудия. Сегодня способность судов обеспечивать справедливое и быстрое правосудие для своих..

    Введение в машинное обучение для обнаружения аномалий (часть 1)
    Тщательно созданный, тщательно спроектированный ресурс для специалистов по данным. Часть 1 Главы 03 из Руководства по машинному обучению для обнаружения аномалий Внимание! Прежде чем вы..

    Начало работы с Pulumi в Digital Ocean
    Цифровой океан (ДО) — отличная альтернатива многим другим поставщикам облачных услуг. DO предоставляет простой и понятный пользовательский интерфейс, упрощающий управление инфраструктурой и..