[Homework] Java OOP - Въпрос относно Problem 1. Geometry
Здравейте,
започнах да пиша домашното към темата, и имам един въпрос относно задача 1:
Ето как съм конструирал проекта до тук:
- абстрактен клас Vertex с наследници Vertex2d/Vertex3d
- абстрактен клас Shape с абстрактни наследници PlaneShape/SpaceShape и всички конкретни форми като наследници
Как точно ще стане това абстрактния клас Shape да има лист от Vertex а неговите абстрактни наследници PlaneShape и SpaceShape да имат листове от сътвтно Vertex2d и Vertex3d ? Проблема е че когато добавя полето лист от вектори в Shape заедно с конструктор и после опитам в PlaneShape да преизползвам родителския конструктор , с лист от Vertex2d и ми казва че е невъзможно...
Май го написах доста объркващо, но ако някой ме разбере нека пише :)
Абсолютно си ме разбрал правилно! :)
Благодаря за подсказката - продължавам по задачата.
Поздрави!
Надявам се, че си ме разбрал, че не ми е много подробно обяснението. В списък от Vertex може да вкарваш Vertex2D, но в променлива List<Vertex> не може да сложиш List<Vertex2D>, защото второто не е наследник на първото, и двете са списъци.
Ами разбрах как да реша проблема :)
А сега след втория ти пост, вече разбрах и какъв е проблема. То близко да акъла, но кой д се сети..
А и интелито не помага много .. само ми пишеше - едното в другото не влиза и до там.
Благодаря ти отново!
Добре, нека обобщя какво съм разбрал:
в класа Shape правя поле List<Vertex> и сетър за това поле за да може да се достъпва от наследниците, без конструктор.. ?
в абстрактните наследници правя конструктор, който приема лист от точки (2д, 3д) и посредством сетъра на родителя пълни неговия лист
в конкретните наследници, нищо по различно - конструктор който приема лист от точки ...
и съответно, когато искам да създам обект от конкретен тип - например триъгълник, трябва да му подам като аргумент на конструктор Лист от точки ?
Работиш със списък, това е референтен тип, за да добавяш елементи към него ти трябва гетър, сетър е нужен само ако искаш да смениш обекта.
Това, което си представях, е да има метод в Shape, който взима като параметри някакви точки и ги добавя в списъка. Така в PlaneShape да речем получаваш List<Vertex2D>, подаваш го на наследения метод, който взима точките и ги пълни в полето List<Vertex>. В базовия конструктор просто ще се инициализира списъка, а вече в наследниците ще се вика метода за пълненето му. По-умен начин в момента не се сещам.
Помня я тази задача, един колега от курса тогава го повдигна точно този въпрос и 2 часа се чудехме защо не може, защото на пръв поглед изглежда правилно като полиморфизъм, докато не се усетиш че ти презаписваш целият лист, а не вкарваш елементи в него. Иронията е че може да се счупи и да мине без да те предупреди компилатора, ако използваш трансформиращ метод който връща Generic колекция като Array.asList примерно (не го прави разбира се ).
При Vertex2D extends Vertex && Vertex3D extends Vertex
и
поле List<Vertex> vertices => пропърти List<vertex> getVertices();
можеш да добавяш чрез getVertices().add(new Vertex3D(...));
Ако искаш да подаваш цял лист от други типове, ще трябва да имаш логика, която минава през всеки един от елементите и ги добавя към листа от други елементи, както Фил спомена.
Мисля, че същото нещо може да се постигне и с дженерици? Ако имаш клас, който е дженерик при <V extends Vertex> и имаш лист от същия дженерик (List<V>). Щом веднъж си вигнал класа със V което е Vertex2D то и листът ще стане List<Vertex2D>.
Честно да ти кажа, не съм много наясно с ООП-то като цяло. Имах възможност и време да гледам 3-4 лекции от минали курсове и каквото съм прочел из интернет..
така че това със дженерик класа, няма да мога да го направя към момента :)
Стигнах до следното решение:
Vertex2D/Vertex3D наследяват Vertex
В класа Shape имам поле List<Vertex> , и следния метод:
PlaneShape/SpaceShape наследяват Shape, и ето как изглежда PlaneShape класа:
Ето пример и как изглежда конкретен клас, в случая Triangle:
И ето какво се случва в мейн:
И след овърлоудване на тостринг изхода е нещо подобно:
Има ли нещо вярно в това което съм написал? :)
Нещата изглеждат ок, само че нагласи accesor-a на getVertices метода да е protected не public, и не мисля че ти трябва да пазиш, точките като полета в триъгълника, нали листа от Vertex-и е за това да ти пази точките, вместо това си направи полета за страните в триъгълника и ги смятай в конструктора, за да не се налага да ги преизчисляваш при всяка операция и конструктора на planeShape го направи protected (конструкторите на абстрактни класове не трябва да са public). Сега като се замисля то по добре направи листа във Shape протектед, ще си спестиш бая проблеми така.
Благодаря за коментарите!
Относно триъгълника (защото реално , той е единствената форма , която ползва върховете си за пресмятане на Area), ако не му запазя върховете като полета, после при пресмятането на Area, трябва да итерирам през листа, за да вземса стойностите, нали така?
EDIT: Сега заграях, че просто трябва да сменя формулата за пресмятане на лице - да позлват страните а не върховете
ПС. Това ми е метода за пресмятане на лице:
И става много интересно в момента, в който искам да направя една проверка при инициализирането на точките на триъгълника => ако са повече от три да хвърля Exception. Не мога да достъпя полето List от клас Shape защото е private немога да го сложа и protected защото в Java нали protected работело по различен начин...трябва да го слагам public което също е тотално грешно.
Може би най-добрият подход е да има само базов конструктор без параметри и отделен метод .AddAll например, който да може да се @Override и там вече да се сложи проверката.
ОК е да сложиш protected. :-)) Иначе би трябвало да имаш достъп до листът, който ти се подава в конструктура - не можеш ли да провериш него?
Финално стигнах до това:
https://github.com/vdonchev/Geomtery/tree/master/src