В чем разница между классом и типом в Scala (и Java)?

Scala

Где могут наблюдаться различия между классом и типом в Scala и почему это различие важно?

Это только соображение от точки зрения дизайна языка или имеет "практическое" влияние при программировании Scala?

Или это фундаментально для "обеспечения границ" системы типов (Nothing, Null приходят мне на ум)?

Java

Сколько из рассмотренных выше соображений/различий/проблем может быть распознано в Java?


(См. В чем разница между типом и классом? как введение в язык-агностик.)

Ответ 1

Когда вы говорите "тип", я собираюсь предположить, что вы в основном ставите статический тип. Но я скоро расскажу о динамических типах.

Статический тип является свойством части программы, которая может быть статически проверена (статические средства "без запуска" ). В статически типизированном языке каждое выражение имеет тип, записываете ли вы его или нет. Например, в Cish "int x = a * b + c-d", a, b, c и d имеют типы, a * b имеет тип, a * b + c имеет тип и a * b + c -d имеет тип. Но мы только аннотировали x с типом. На других языках, таких как Scala, С#, Haskell, SML и F #, даже это необязательно.

Точно, какие свойства доказуемы, зависит от проверки типа.

Класс стиля Scala, с другой стороны, является просто спецификацией для набора объектов. Эта спецификация включает некоторую информацию о типе и включает в себя множество деталей реализации и представления, таких как тела методов и частные поля и т.д. В Scala класс также указывает некоторые границы модулей.

Многие языки имеют типы, но не имеют классов, а многие языки имеют классы, но не имеют (статических) типов.

Существует несколько наблюдаемых различий между типами и классами. Список [String] - это тип, но не класс. В Scala List - это класс, но обычно это не тип (на самом деле это более высокий тип). В С# List не является типом какого-либо типа, а в Java это "сырой тип".

Scala предлагает структурные типы. {def foo: Bar} означает любой объект, который, предположительно, имеет метод foo, который возвращает строку, независимо от класса. Это тип, но не класс.

Типы могут быть абстрагированы с использованием параметров типа. Когда вы пишете def foo [T] (x: T) =..., то внутри тела foo T есть тип. Но T не является классом.

Типы могут быть виртуальными в Scala (т.е. "члены абстрактного типа" ), но классы не могут быть виртуальными с Scala сегодня (хотя там есть шаблонный способ для кодирования виртуальных классов https://wiki.scala-lang.org/display/SIW/VirtualClassesDesign)

Теперь динамические типы. Динамические типы - это свойства объектов, которые среда выполнения автоматически проверяет перед выполнением определенных операций. В динамически типизированных языках OO на основе классов существует сильная корреляция между типами и классами. То же самое происходит на языках JVM, таких как Scala и Java, которые имеют операции, которые могут быть проверены только динамически, такие как отражение и литье. В этих языках "стирание типа" более или менее означает, что динамический тип большинства объектов совпадает с их классом. Более или менее. Это не соответствует, например, массивам, которые обычно не стираются, так что среда выполнения может отличать Array [Int] и Array [String]. Но помните, что мое широкое определение "динамические типы - это свойства объектов, которые автоматически проверяет среда выполнения". Когда вы используете отражение, можно отправить любое сообщение на любой объект. Если объект поддерживает это сообщение, тогда все работает. Таким образом, имеет смысл говорить обо всех объектах, которые могут взломать как утку как динамический тип, хотя это не класс. Это суть того, что сообщества Python и Ruby называют "утиным языком". Кроме того, по моему широкому определению даже "нулевая" - это динамический тип в том смысле, что в большинстве языков среда выполнения автоматически проверяет числа, чтобы убедиться, что вы не делите на ноль. Существует очень и очень мало языков, которые могут доказать, что статически, ставя нулевой (или не нулевой) статический тип.

Наконец, как уже отмечали другие, существуют такие типы, как int, которые не имеют класса в качестве детали реализации, такие как Null и Any, которые немного особенные, но у них есть классы и нет, и типы вроде Nothing который даже не имеет значений, не говоря уже о классе.

Ответ 2

Хорошо, я укушу... У Джеймса хороший ответ, поэтому я собираюсь попробовать другой такт и дать более приблизительную точку зрения.

В общем, класс - это нечто, что можно создать. Singleton objects (scala) черты (scala) и интерфейсы (scala) также обычно рассматриваются как классы. Это имеет смысл, поскольку синглтоны все еще создаются (с помощью кода, генерируемого компилятором), и интерфейс может быть создан как часть подкласса.

Это приводит нас ко второму вопросу. классы являются основной единицей дизайна на большинстве объектно-ориентированных языков (хотя и не на основе прототипов, таких как javascript). Полиморфизм и подклассы определяются как классы. классы также предоставляют элементы управления пространством имен и видимости.


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

(Int) => String // both the type and class are Function1[Int,String]
"hello world" // class and type are String    

Вы также получите некоторые интересные различия между Scala и Java:

7 // both the class and type are Int in Scala
  // in Java there no class and the type is Integer.TYPE

println("hello world") // the return type is Unit, of class Unit
                       // Java has void as a type, but no corresponding class

error("oops") // the type and class are both "Nothing"

и действительно забавные типы, которые вообще не являются классами. Например, this.type всегда ссылается на уникальный тип this. Он уникален для одного экземпляра и даже не совместим с другими экземплярами одного и того же класса.

Существуют также абстрактные типы и параметры типа. Например:

type A // 'A' is an undetermined abstract type
       // to be made concrete in a subclass

class Seq[T] { ... } // T is a type, but not a class

Seq интересен как класс, а не тип. Точнее, это "конструктор типов"; то, что будет создавать допустимый тип, если он снабжен необходимым параметром типа. Еще один термин для конструкторов типов - "более высокие типы типов", мне лично не нравится этот термин, поскольку "конструктор типов" побуждает меня думать о поставках таких типов, как любая другая форма аргумента, - ментальная модель, которая хорошо меня обслуживала для Scala.

"более высокий" правильно подразумевает, что Seq имеет "вид", который равен * => *, это обозначение означает, что Seq возьмет один тип и даст один тип (это похоже на точечные обозначения для описания функций). Для сравнения, тип Map равен * => * => *, потому что он принимает два параметра типа.

Ответ 3

Тип может быть полезен сам по себе, без каких-либо экземпляров. Один пример для этого называется "phantom type". Вот пример для Java: http://michid.wordpress.com/2008/08/13/type-safe-builder-pattern-in-java/

В этом примере мы имеем public static class Initializer<HA, HB>, где HA и HB принимают некоторые типы (представленные абстрактными классами TRUE и FALSE), не создавая экземпляров.

Надеюсь, это показывает, что типы и классы - это нечто иное, и эти типы могут быть полезны сами по себе.

Ответ 4

(только Java) Я бы сказал, что тип - это набор объектов. Объектом o является тип X, если o является членом набора X. Тип X является подтипом Y, если set X является подмножеством Y.

Для каждого класса C (не интерфейса) существует набор объектов, созданных из new C(...). Интересно, что мы редко заботимся об этом. (но каждый объект принадлежит к набору, подобному этому, что может быть полезно)

Для каждого класса C существует тип t(C), обычно называемый "типом C", который представляет собой набор всех объектов, которые могут быть созданы из new S(...), где S является C или подклассом C.

Аналогично, для каждого интерфейса я существует тип t(I), "тип I", который представляет собой набор всех объектов, которые могут быть созданы из new S(...), где S реализует I.

Очевидно, что если класс S является подклассом C, тип S является подтипом типа C. Аналогично для интерфейса I

Существует пустой тип, который является пустым. Нулевой тип - это подтип каждого типа.

Существует набор всех объектов, который является типом Object. Это супер тип каждого типа.

До сих пор этот формализм довольно бесполезен. Тип в основном такой же, как класс или интерфейс, а отношение подтипа в основном относится к подклассу/подинтерфейсу. Тривиальность - это хорошо, язык понятен! Но вхождение в generics, есть более сложные типы и операции, такие как объединения и пересечения типов. Типы - это не только классы и интерфейсы, а отношения подтипов намного богаче и труднее понять.