Пакет go-bindata выглядит так, как будто вас интересует.
https://github.com/go-bindata/go-bindata
Это позволит вам преобразовать любой статический файл в вызов функции, который может быть встроен в ваш код и будет возвращать байтовый фрагмент содержимого файла при вызове.
Наличие однокорневой иерархии типов может быть полезно по-разному. В частности, до появления дженериков это был единственный способ работы чего-то вроде ArrayList
. С дженериками он дает значительно меньше преимуществ - хотя я подозреваю, что он все еще может быть полезен в некоторых ситуациях. РЕДАКТИРОВАТЬ: Например, модель построения LINQ to XML очень "свободна" с точки зрения указания через объект
... но работает очень хорошо.
Что касается наследования из разных классов - вы получаете напрямую из одного класса, но он, в свою очередь, будет производным косвенно из другого, и так далее до Object.
Обратите внимание, что вещи, которые «имеют все объекты общего», такие как хэш-код, равенство и мониторы, считаются еще одним дизайнерским решением, в разумности которого я сомневаюсь. Без единой корневой иерархии эти проектные решения, возможно, не были бы приняты таким же образом;)
Тот факт, что каждый класс наследует объект, обеспечивается компилятором.
То есть, если вы напишете:
class A {}
Это будет компилироваться как:
class A : Object{}
Но если вы укажете:
class B : A {}
Object
будет в иерархии B
, но не напрямую - так что множественного наследования по-прежнему нет.
Что ж, множественное наследование объекта
не применяется - вы можете думать об этом как:
«Если тип не имеет базового типа, тогда неявно внедрить Object
".
Таким образом, применяя правило ad-nauseam, все типы наследуются от объекта один раз и только один раз - поскольку внизу иерархии должен быть тип, не имеющий основы; и, следовательно, который будет неявно унаследован от Object
.
Что касается того, почему эти языки / фреймворки имеют эту особенность, у меня есть несколько причин:
1) Подсказка в названии «Объектно-ориентированный». Все является объектом, поэтому все должно иметь в своей основе «объект» (или эквивалент), иначе принцип дизайна будет нарушен с самого начала.
2) Позволяет фреймворку предоставлять перехватчики для общих операций, которые все типы должны / могут нуждаться в поддержке.Например, генерация хэш-кода, вывод строки для отладки и т. Д.
3) Это означает, что вы можете избежать использования неприятных приведений типов, которые могут нарушить работу - например, (((int *) (void *)) value)
- так как у вас есть хороший дружелюбный супертип для всего
Там, вероятно, гораздо больше, чем это - и за то время, которое мне потребовалось, чтобы написать это, было опубликовано 6 новых ответов; так что я оставлю это здесь и надеюсь, что люди лучше меня смогут объяснить более подробно и, возможно, лучше :)
У вас есть базовый класс Object, в том числе и потому, что у класса Object есть методы (например, в .NET, GetHashCode()
, которые содержат общую функциональность, которую должен иметь каждый объект).
Множественное наследование действительно невозможно, но можно вывести класс A из класса B, потому что A может не выводиться напрямую из Object, но B выводится, поэтому все классы в конечном счете выводятся из Object, если зайти достаточно далеко в иерархии наследования класса.
Что касается первой части вашего вопроса, это то, как классы получают общие свойства и методы. Также так мы можем иметь сильно типизированные параметры для функций, которые могут принимать любой объект.
Что касается второго вопроса, то вы просто выводите свой класс от другого класса; тогда он будет потомком этого класса, который, в свою очередь, является потомком Object. Никакого конфликта нет.
Вкратце
1) Класс Object определяет базовое состояние и поведение, которыми должны обладать все объекты, например, способность сравнивать себя с другим объектом, преобразовывать в строку, ждать переменную условия, уведомлять другие объекты об изменении переменной условия и возвращать класс объекта.
2) Вы можете иметь B расширять C, а A расширять B. A - дочерний класс B, а B - дочерний класс C. Естественно, A также является дочерним классом C.
.Для сравнения давайте посмотрим на язык, который не использует единственный корневой класс - Objective-C. В большинстве сред Objective-C будут доступны три корневых класса (Object
, NSObject
и NSProxy
), и вы можете написать свой собственный корневой класс, просто не объявляя суперкласс. На самом деле Object
является устаревшим и существует только по причинам наследия, но это информативно, чтобы включить его в это обсуждение. Язык является duck typed, поэтому вы можете объявить тип переменной как "любой старый объект" (записывается как id
), тогда даже не важно, какой корневой класс она имеет.
Хорошо, у нас есть все эти базовые классы. На самом деле, даже для того, чтобы компилятор и библиотеки времени выполнения могли куда-либо добраться, им необходимо некоторое общее поведение: все корневые классы должны иметь указатель ivar под названием isa
, который ссылается на структуру определения класса. Без этого указателя компилятор не знает, как создать структуру объекта, а библиотека времени выполнения не знает, как узнать, к какому классу относится объект, каковы его переменные экземпляра, на какие сообщения он реагирует и так далее.
Поэтому, хотя Objective-C утверждает, что у него несколько корневых классов, на самом деле есть некоторое поведение, которое должны реализовывать все объекты. Таким образом, кроме названия, существует общий примитивный суперкласс, хотя и с меньшим API, чем java.lang.Object
.
N.B. Как оказалось, и NSObject
и NSProxy
действительно предоставляют богатый API, подобный java.lang.Object
, через протокол (как интерфейс Java). Большинство API, претендующих на работу с типом id
(помните, это тип "любой старый объект"), на самом деле будут считать, что они отвечают на сообщения в протоколе. К тому времени, когда вам действительно нужно использовать объект, а не просто создавать его компилятором, оказывается полезным сложить все это общее поведение, такое как равенство, хэширование, описание строк и т.д., в корневой класс.