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

Scala

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

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

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

Джава

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


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

4 ответа

Решение

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

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

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

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

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

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

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

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

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

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

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

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

Вообще говоря, класс - это то, что может быть создано. черты одноэлементных объектов (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's 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 Интересно, так как это класс, но не тип. Точнее, это "конструктор типов"; что-то, что создаст допустимый тип, если он снабжен необходимым параметром типа. Другой термин для конструкторов типов - "типы с более высоким родом", мне лично этот термин не нравится, так как "конструктор типов" побуждает меня думать с точки зрения предоставления типов, как любая другая форма аргумента, - ментальная модель, которая хорошо мне послужила для Скала.

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

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

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам