В чем разница между классом и типом в Scala (и Java)? - PullRequest
52 голосов
/ 17 февраля 2011

Скала

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

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

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

Java

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


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

Ответы [ 4 ]

44 голосов
/ 17 февраля 2011

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

Статический тип - это свойство части программы, которое может быть статически доказано (статический означает «без запуска»). В статически типизированном языке каждое выражение имеет тип независимо от того, пишете вы его или нет. Например, в 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 / дисплей / SIW / VirtualClassesDesign )

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

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

24 голосов
/ 18 февраля 2011

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

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

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

3 голосов
/ 17 февраля 2011

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

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

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

1 голос
/ 17 февраля 2011

(только 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. Это супер тип каждого типа.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...