двумерный массив универсального типа
Вот решил тут прокачаться в Java, обзавелся парой умных книжек, но столкнулся с проблемой.
protected Cell[][] field;
...
public Map(int field_size) {
field = new Cell[field_size][field_size];
}
}
Но вот тут получаю "error: generic array creation".
Порывшись в интернетах нашел вот такой костыль.
Есть ли метод более очевидный и естественный?
ЗЫ: (для самопроверки) при создании такого массива, как я понимаю, конструкторы не вызываются и создается лишь массив ссылок, но не самих объектов, верно?
field = (Cell[][])new Object[field_size][field_size];
for(Cell[] row : field)
for(Cell cur : row)
cur = new Cell();
}
Получаю "error: unexpected type. required: class. found: type parameter Cell".
Это дело основательно ввело меня в ступор... Уважаемые эксперты, подскажите где я начудачил или как это побороть!?
protected Cell[][] field;
...
почему бы не написать просто
protected AbstractCell[][] field;
...
?
field = (Cell[][])new Object[field_size][field_size];
for(Cell[] row : field)
for(Cell cur : row)
cur = new Cell();
}
Получаю "error: unexpected type. required: class. found: type parameter Cell".
error на строчке cur = new Cell(); ? ; )
2. да, верно, именно на этой строчке.
Мой друг, вы ошибаетесь. Ее просто надо уметь готовить :)
Во-первых:
Конструкторы обьектов не вызываются. Масив будет заполнен нулами. Но вызывается коструктор массива. А поскольку джава язик довольно безопасный то масиву уже при его создании просто необходимо знать класс своих елементов. И тут надо сделать важное замечание - Cell не есть классом. Ето только место для подстановки класса. Это накладывает свое играничения с которыми вы столкнулись.
Во-вторых:
поскольку ваш пример (как на меня) лишен всяческой смысловой нагрузки, то осмелюсь паредположить что он суто тренировочный. Тогда позвольте заметить что вы начали експерименты в довольно сложной области дженериков. Там много ограничений, большинство которых розруливается, но для етого надо хорошо ориентироваться в роботе компилятора и виртуальной машины. Тоесть дженерики - ето не то с чего следует начинать знакомство для начинающего. Хотя вы уже не первый год замужем и так как вам учить операторы смысла нет - то я понимаю почему вы играетесь с дженериками. Советую вам почитать главу про дженерики в книге Хорстмена и Корнела по 5 или 6 джаве. Там эти ограничения красиво описаны (вообще хорошая книга которая обясняет именноо детали. В том числе и особенности каста масива с которым вы тоже столкнулись. Советую внимательно читать заментки с знаком восклицания. Все самое интересное именно там).
В-третьих:
ошибка на new Cell(); - еще не приговор.
следующий пример не совсем то что вам нужно так как в нем класс GenTest уже не есть дженериком, а только наследуется от дженерика, но всеже думаю пример будет вам интересным. Такой подход используют, к примеру, при создание ДАО для роботы с сущностями бд. Базовый абстрактный клас ДАО умеет делать инсерт, апдейт, делит, поиск по ид. А для каждой конкретной сущности делается недженерик потомок от абстрактного дженерика со своими дополнительными методами-селекторами.
import java.lang.reflect.ParameterizedType;
class AbstractGenTest<T> {
protected Class<T> persistentClass;
public AbstractGenTest() {
persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
public T createNewInstance() throws InstantiationException, IllegalAccessException {
return persistentClass.newInstance();
}
}
public class GenTest extends AbstractGenTest<TestClass>{
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
GenTest genTest = new GenTest();
genTest.createNewInstance();
}
}
class TestClass {
public TestClass() {
super();
System.out.println("TestClass created");
}
}
а стоило ли столько писать, учитывая, что все это расписано в книге???
во вторых: даже классика Core Java Хорстмана, конечно внесет ясность в восприятие картины, но не научит, как именно и главное в какой ситуации правильно пользоваться тем или иным паттерном. это касается не только generics, но и всего остального. лучше всего это объясняется именно в Effective Java, о чем я и упомянал выше. Здается мне, что вы ее не читали. Да кстати, массивы с generic кодом никогда не миксуют, это грубая ошибка.
какой бы язык вы не учили, в class-room'ах (предположим даже Английский) даются лишь базовы знания, общие правила и т.д. Но как правильно пользоваться языком и как объясниться на этом языке вас не научат ни в одном class-room'e, а это самый важный аспект любого языка.
А я люлю пописать на досуге :)
Не читал. Но раз вы так советуете, то прочитаю. А как вы так точно определили что не читал?
Кстати раз уж розговор зашел о дженериках.
Могу ли я както рефлексией проделать нечто подобное моему примеру в предыдущем посте, но без класса потомка, а прямо с дженерика?
а я телепат :D
шутко конеш... ваши довыды вполне обстоятельны и правдоподобны, но.... :)
а Effective Java - это книжка ну просто Must to Read, для любого профессионального Java developer'a.
читал я Core Java оба тома: после, появилось просветление, что такое вообще Java и с чем ее едят (перед этим читал книгу Java Tutorials - полный сумбур - это антипаттерн! даже не тратьте время!). Все довольно обстоятельно, последовательно и ясно изложено в Core Java.
После прочтения Effective Java: пришло озарение, как же правильно программировать на Java и как это легко. Мда и после, просматривал sample apps из Core Java и обнаружил, что Horstmann в некоторых местах грубо нарушает основополагающие паттерны, на кот. строиться сам язык Java. в Effective Java приводяться постонно примеры из стандартной библиотеки Java и пояснятеся почему было принято именно такое решение, а не иное другое - ошибочне (примеры ошибочного кода так же приводяться).
На последок, автор Joshua Bloch - на текущий момент главный Java архитектов в компании Google, ранее работавший в Sun и приложил не мало усилий в создание стандартной библиотеки java вплоть до версии 1.5. Обещаю, после прочтения вы все сами поймете :)
Спорить не стану, так как листингов, впрочем как и описаний АПИ, не читал воовсе. Меня в то время интересовали именно детали синтаксиса и всякого рода мелочи, так как я сежжал с РНР на Джаву.
И должон сказать что многое из прочитаного мне пригодилось.
Например описание того что произойдет если есть try/cache/finally много ретурнов внутри, специфыка нестед класов и анонимных классов, особенности сериализации и десериализации сложных структур данных и т.п. . Возможно в Effective Java эти вопросы тоже россматриваются и даже с более правильной стороны. Собственно я чувствую что у меня есть некая бреш в знаниях именно в использовании языка на таком уровне.
Ну и раз уже о книгах говорим то каково ваше мнение о роботах мартина Фаулера "Рефакторинг" и "Архитектура корпоративных программных приложений" (если читали).
Мое мнение о етих книгах в общих чертах положительное, но кажется что уж очень много кода там в примерах. Тоже много пропускал. А про вторую книгу - ее полезно читать после знакомства с несколькими существующими технологиями в которых используются схожые подходи (иначе можно и недопонять).
[COLOR=Silver]
[/COLOR]
Кстати раз уж розговор зашел о дженериках.
Могу ли я както рефлексией проделать нечто подобное моему примеру в предыдущем посте, но без класса потомка, а прямо с дженерика?
хоть рефлексия это и медленный способ и его нужно стараться всячески избегать (об этом Хорстманн тоже пишет, но сам часто использует рефлексию в своих samples apps....), но думаю можно лишь изредка...
Generics обеспечивает безопастность типов и необходимо строить классы каждый раз исходя из соображений безопасности, что бы злостный юзер ваших классов не смог скомпрометировать ваш класс с помошью рефлекции! потому как имеется куча способов сделать это.
ну маленький пример где вступает в дело рефлекция, при этом type safe обеспечена:
import java.lang.reflect.*;
public class PrintAnnotation {
// Use of asSubclass to safely cast to a bounded type token
static Annotation getAnnotation(AnnotatedElement element,
String annotationTypeName) {
Class<?> annotationType = null; // Unbounded type token
try {
annotationType = Class.forName(annotationTypeName);
} catch (Exception ex) {
throw new IllegalArgumentException(ex);
}
return element.getAnnotation(
annotationType.asSubclass(Annotation.class));
}
// Test program to print named annotation of named class
public static void main(String[] args) throws Exception {
if (args.length != 2) {
System.out.println(
"Usage: java PrintAnnotation <class> <annotation>");
System.exit(1);
}
String className = args[0];
String annotationTypeName = args[1];
Class<?> klass = Class.forName(className);
System.out.println(getAnnotation(klass, annotationTypeName));
}
}
public class Stack<E> {
private E[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
// The elements array will contain only E instances from push(E).
// This is sufficient to ensure type safety, but the runtime
// type of the array won't be E[]; it will always be Object[]!
@SuppressWarnings("unchecked")
public Stack() {
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(E e) {
ensureCapacity();
elements[size++] = e;
}
public E pop() {
if (size==0)
throw new EmptyStackException();
E result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}
public boolean isEmpty() {
return size == 0;
}
private void ensureCapacity() {
if (elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
// pushAll method without wildcard type - deficient!
// public void pushAll(Iterable<E> src) {
// for (E e : src)
// push(e);
// }
// Wildcard type for parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {
for (E e : src)
push(e);
}
// popAll method without wildcard type - deficient!
// public void popAll(Collection<E> dst) {
// while (!isEmpty())
// dst.add(pop());
// }
// Wildcard type for parameter that serves as an E consumer
public void popAll(Collection<? super E> dst) {
while (!isEmpty())
dst.add(pop());
}
// Little program to exercise our generic Stack
public static void main(String[] args) {
Stack<Number> numberStack = new Stack<Number>();
Iterable<Integer> integers = Arrays.asList(3, 1, 4, 1, 5, 9);
numberStack.pushAll(integers);
Collection<Object> objects = new ArrayList<Object>();
numberStack.popAll(objects);
System.out.println(objects);
}
}
[/COLOR]
вот именно. в этом и вся соль.
Книжка Мартина Фаулера это хорошее пособие в глубь тематики разбора кода. Вообщем, согласен - очень хорошие впечатления остались. Но теперь за меня всю подобную работу делает Intellij IDEA :)
Про вторую ничего сказать не могу.. я не Enterprise developer и это направление меня не сильно интересует. мое направление - Java Desktop Apps, Desktop integration solutions и Java2D. вообщем десктоп.
недавно принял решение переквалифицироваться в Android Engineer. 2 книги уже приобрел, теперь нужно время прочесть и некоторую практику накопить.
почему бы не написать просто
protected AbstractCell[][] field;
...
?
потому что в умных книжках пишут, что raw-типы - это плохо))