Объявление функции в протоколе дает указание компилятору использовать динамическую отправку при вызове функции, поскольку компилятор ожидает, что типы, реализующие протокол, предоставят реализацию для этой функции. Это называется method requirement
. Если тип не определяет метод, то среда выполнения разрешает вызов метода методу, объявленному в расширении протокола.
Объявление функции только в расширении протокола сообщает компилятору, что он не нужен использовать динамическую отправку и использовать статическую диспетчеризацию, которая выполняется быстрее, но не очень хорошо работает с полиморфизмом, поскольку будет реализована реализация расширения протокола.
Чтобы проиллюстрировать выше, рассмотрим следующий код:
protocol Shape {
func draw()
}
extension Shape {
func draw(){
print("This is a Shape")
}
}
struct Circle: Shape {
func draw() {
print("This is a Circle")
}
}
struct Square: Shape {
func draw() {
print("This is a Square")
}
}
let shapes: [Shape] = [Circle(), Square()]
for shape in shapes {
shape.draw()
}
Вышеприведенный код будет иметь выход
This is a Circle
This is a Square
Это связано с тем, что draw()
является method requirement
, что означает, что при вызове draw()
, среда выполнения будет искать реализацию draw ()
в действительном типе элемента, в данном случае внутри Circle
и Square
.
Теперь, если мы не объявим draw
как требование метода, что означает, что мы не упоминаем его в объявлении протокола
protocol Shape {
}
. Затем компилятор больше не будет использовать динамическую отправку и перейдет прямо к реализации, определенной в расширение протокола. Таким образом, код будет печатать:
This is a Shape
This is a Shape
Подробнее, если мы сбрасываем листинг элемента массива на тип, который, как мы ожидаем, он будет, мы получим статическое связывание назад. Это напечатает This is a Circle
if let circle = shapes[0] as? Circle {
circle.draw()
}
, потому что компилятор теперь может сказать, что первый элемент из shapes
является Circle
, а поскольку Circle
имеет метод draw()
, он будет вызывать это.
Это способ Swift справиться с абстрактными классами: он дает вам возможность указать, что вы ожидаете от типов, соответствующих этому протоколу, и при этом допускать реализацию по умолчанию этих методов.
Я знаю, что немного поздно для ответа, но я нашел это обходное решение полезным для моего случая... Надежда это помогает!
Использование массив HashMap для хранения HashMaps..
public static void main(String[] args) {
HashMap[] arr = new HashMap[1];//creating an array of size one..just for sake of example
HashMap<String, String> arrMap = new HashMap<String, String>();
//use loops to store desired key-value pairs into the HashMap which will be stored inside the array
arrMap.put("ABC", "Name");
//use loop to store your desired hashMap into the array at a particular index
arr[0] = arrMap;
//desired manipulation of the stored array.
System.out.println(arr[0]);
}
Не совсем ответ на ваш вопрос, но рассматривали ли вы возможность использовать вместо него список
?
List<Map<String,Integer>> maps = new ArrayList<Map<String,Integer>>();
...
maps.add(new HashMap<String,Integer>());
, похоже, работает нормально.
См. Java теория и практика: подводные камни Generics для подробного объяснения того, почему смешивание массивов с generics не рекомендуется.
Как упоминалось Дрю в комментариях, может быть даже лучше использовать Collection интерфейс вместо Список
. Это может пригодиться, если вам когда-нибудь понадобится перейти на Set
или один из других подынтерфейсов из Collection
. Пример кода:
Collection<Map<String,Integer>> maps = new HashSet<Map<String,Integer>>();
...
maps.add(new HashMap<String,Integer>());
С этой отправной точки вам нужно будет только изменить HashSet
на ArrayList
, PriorityQueue
или любой другой класс, реализующий ] Коллекция
.
You can't safely create a generic array. Effective Java 2nd Edition goes into the details in the chapter on Generics. Start at the last paragraph of page 119:
Why is it illegal to create a generic array? Because it isn’t typesafe. If it were legal, casts generated by the compiler in an otherwise correct program could fail at runtime with a
ClassCastException
. This would violate фундаментальная гарантия, предоставляемая система общих типов.Чтобы сделать это более конкретным, рассмотрим следующий фрагмент кода:
// Почему создание универсального массива недопустимо - не компилируется! Список
[] stringLists = новый список [1]; // (1) Список <Целое число> intList = Arrays.asList (42); // (2) Object [] objects = stringLists; // (3) объекты [0] = intList; // (4) Строка s = stringLists [0] .get (0); // (5) Давайте представим, что строка 1, которая создает общий массив, допустимо. Строка 2 создает и инициализирует
Список
, содержащий единственный элемент. Строка 3 хранитПеречислить массив
вобъект
переменная массива, что допустимо, потому что массивы ковариантны. Линия 4 магазиновСписок
в единственную element of theObject
array, which succeeds because generics are implemented by erasure: the runtime type of aList
instance is simplyList
, and the runtime type of aList
instance is[] List[]
, so this assignment doesn’t generate anArrayStoreException
. Now we’re in trouble. We’ve stored aList
instance into an array that is declared to hold onlyList
instances. In line 5, we retrieve the sole element from the sole list in this array. The compiler automatically casts the retrieved element toString
, but it’s anInteger
, so we get aClassCastException
at runtime. In order to prevent this from happening, line 1 (which creates a generic array) generates a compile-time error.
Because arrays and generics don't combine well (as well as other reasons), it's generally better to use Collection
objects (in particular List
objects) rather than arrays.
Короткий ответ, похоже, таков: вы действительно просто не можете.
См. Следующий блог об этом. http://www.bloggingaboutjava.org/2006/01/java-generics-quirks/
One of the comments to the blog states that:
Actually, the engineers made the creation of such an Array illegal. So the creation of an array from generic Class fails. The Collection.toArray method followed by a Cast to the Array works at compile time.
This solves not the problem, that the ArrayStoreCheck can’t be done during Runtime, but you can create an Array of generics in this way.
As suggested by Bill the Lizard, you probably are better off using a
List<Map<String,Integer>>
I had a similar question, best response I got referred to this
myMaps = new HashMap<String, Integer>[10]();
Значит, это неправильно
Почему бы не составить список карт вместо того, чтобы пытаться создать массив?
List<Map<String, Integer>> mymaps = new ArrayList<Map<String, Integer>>(count);
В целом не рекомендуется смешивать универсальные шаблоны и массивы в Java, лучше использовать ArrayList.
Если вы должны использовать массив, лучший способ справиться с этим - поместить создание массива (ваш пример 2 или 3) в отдельный метод и аннотируйте его с помощью @SuppressWarnings ("unchecked").