ზოგადი ტიპები

                             
                  
ზოგადი ტიპების საჭიროება წარმოიშვა ჯავას კოლექციებთან მუშაობისთვის, კოლექციები თქვენთვის ნაცნობია როგორც სტრუკტურული მონაცემები, ისეთი როგორიც არის LinkedList, ArrayList, HashMap, Set და სხვანი. პროგრამისტებს ადრეულ ეტაპზე (ჯავა 5-ე) უწევდათ ობიეკტის ტიპის კოლექციებთან მუშაობა, რაც არც ისე უსაფრთხო გახლდათ, რადგან: 1: სტუკტურული მონაცემი ახდენდა გენერირებას ნებისმიერი ტიპის ობიეკტ ტიპად. 2. ობიეკტის ტიპი საშუალებას აძლევდა პროგრამისტს სტრუკტურისთვის დაემატებინა ყველა ტიპის დატა. 3. ყველა ტიპის დატის დაბრუნების შემთხვევაში საჭირო იყო კასტინგი რომელსაც ClassCastException-ე მიყავდა პროგრამა.  ამ პრობლემების აღმოფხვრის მიზნით და პროგრამისტების მოთხოვნის გათვალისინებით, 1998 წელს შეიქმნა Generics და 2004 წელს ჯავა 5-ის გამოსვლიდან დაემატა სტანდარტულ ბიბლიოთეკას. ამჟამად კოდირების დროს, კოლექციასთან მუშაობისთვის პროგრამისტს უწევს კონკრეტული ტიპის დატა გამოიყენოს, ისეთები როგორიც არის, სია ინტეგრალური ღირებულებებით, სია String ღირებულებებით და ა.შ

    კოლექცია რომელიც  არის ჰეტეროგენული პოტენციური საფრთხის მატარებელია. ამიტომაც დღეს ის არის ჰომოგენური რაც უამრავი პრობლემისგან გვიცავს დასაწყისშივე(At Compile Time).

   ადრეულ ეტაპზე რომ მოგვეხდინა კოლექციასთან უსაფრთხოდ მუშაობა და დაგვეცვა ჰომოგენური პრინციპები, პროგრამისტს უწევდა კონკრეტული კლასების იმპლემენტაცია, მაგალითად თუ სურდა ინტეგრალებთან მუშაობა, მას უნდა შეექმნა ინტეგრალების კლასი, შემდგომ მოეხდინა მისი იმპლემენტაცია, რისი განმეორებაც უწევდა ყველა ოპერაციაში, წარმოიდგინეთ რამოდენა სამუშაოს გაწევა უწევს მსგავს სიტუაციაში პროგრამისტს, უფრო მიზანშეწონილი ვარიანტია შევქმნათ ერთი კლასი, რომელიც იმუშავებს ყველა ტიპთან, განურჩევლად.  სხვა სიტყვებით რომ ვთქვათ, ყველა ჯერზე კლასის შექმნას ჯობია ვიქონიოთ ერთი კლასი და გამოვიყენოთ ის გათვალისწინებულ ან გაუთვალისწინებელ სიტუაციებში. სწორედ ამას ემსახურება ზოგადი ტიპიზირების შექმნა. რომელიც საშუალებას გვაძლევს შევქმნათ ერთი კლასი და მოვახდინოთ მისი ინსტანცირება იმდენი სახით რამდენითაც გვსურს. მაგალითისთვის ნახეთ პაკეტი java.util.*; https://docs.oracle.com/javase/7/docs/api/java/util/List.html ჯავა 5-ან, სადაც შეხვდებით ინტერფეის List-ს, რომელიც გვაძლევს საშუალებას მოვახდინოთ კლასის ტიპიზირება List<Integer>, List<Double> დ ა.შ სადაც <> მეტობა ნაკლებობის ნიშანში მოქცეულ ტიპს ქვია Type Argument (Concrete Type || Actual Type).  ისინი არიან ინსტანციები ზოგადად ტიპიზირებული კლასის პარამეტრების. რომელსაც Type Parameter ქვია. ანუ Generic კლას გააჩნია სინტაქსი <> სადაც მოქცეულია Type Parameter <T>, რომელსაც ინსტანცირების შემდგომ ცვლის <Double> Actual Type Argument.

       მაღლა ავღნიშნე რო ზოგადი ტიპიზირების შექმნის იდეა კოლექციებთან მუშაობისთვის ჩამოყალიბდათქო, მაგრამ ჯავა 5-ან მაღლა შეხვდებით უამრავ კლას და ინტერფეის რომელიც კოლექციაში არ იმყოფება და სადაც ხდება ტიპიზირება კლასების და მეთოდების.

   ამ თავში დეტალურად განვიხილავთ ზოგად ტიპიზირებულ კლასებს, მეთოდებს და ინტერფეისებს, შეხვდებით გამოყენების წესებს, გაიგებთ თუ სად არის მიზანშეწონილი მათი გამოყენება და სად არა.  რას ნიშნავს ტიპი, როგორ ხდება ტიპიზირება, კლასის, ინტერფეისის და მეთოდის, რას ნიშნავს WildCard, Type Erasure, Generics and Inheritance, Heap Pollution, Bridge Methods, Subtyping, Bounds და სხვა მრავალ დეტალს, რომელიც თან სდევს ზოგად ტიპიზირებას. (როგორც წესი ზოგადი ტიპიზირება ყველაზე რთულ თავად ითვლება ჯავა პროგრამირებაში, მისი აღქმა და გაანალიზება დიდ დროს და მუშაობას მოითხოვს, მარტივი მიზეზების გამო, ის არ ემორჩილება ჯავას ფუნდამენტურ წესდებებს. მისი მოვალეობაა კოდის უსაფრთხოება და ამ ამოცანის გადასაჭრელად მას დინების საწინააღმდეგოდ ცურვა უწევს) თუ დაიწყებთ მის სწავლას შუა გზაზე აღარ მიატოვოთ, რა დროც არ უნდა დაჭირდეს, 1 თვე იქნება თუ ერთი წელი, მისი ცოდნით თქვენი კოდი გახდება მოთხოვნადი, მოქნილი და გაანხლებადი, რაც საშუალებას მოგცემთ დაწეროთ მდიდარი აპლიკაციები.

                                                                       სარგებელი

რა საგებელს ვხედავთ ზოგადი ტიპიზირებისგან?

ერორების ნაადრევ ეტაპზე აღმოჩენა არის ერთი-ერთი მთავარი ბენეფიტი ზოგადი ტიპიზირების. ნახეთ მაგალითი:

პარამეტრების გარეშე იღებს ყველაფერს, პარამეტრებით მხოლოდ ინტეჯერს, ალბათ ბევრი იფიქრებს რომ ყველაფრის ქონა ჯობია ბევრად, ერთი ტიპის ქონას, ეს ესე არ არის. სია პარამეტრების გარეშე აბრუნებს ობიეკტს, სია პარამეტრებით აბრუნებს ინტეჯერს, რაც ნიშნავს იმას რომ პარამეტრების გარეშე სიიდან ინფორამცია რომ გამოვიტანოთ გვიწევს კასტინგი, ნახეთ მაგალითი:

მსგავს კასტინგს კი პროგრამა ClassCastException-ე მიყავს. სხვაობა მათ შორის არის ის რომ კონკრეტული ტიპით მუშაობის დროს პრობლემას ვხედავ Compile Time, ობიეკტური ტიპით მუშაობის დროს პრობლემას ვხედავთ Run Time. რაც დასკვნის გაკეთების საშუალებას გვაძლევს, Compile Time პრობლემის გადაჭრა ბევრად მარტივია ვიდრე Run Time პრობლემის გადაჭრა. ნახეთ მაგალითი

სიიდან გამოვიტანეთ ინფორმაცია რომელიც ლონგ ღირებულებას მივანიჭეთ, კომპილირების დროს არ გვაქ ინდიკატორი რომელიც გვეუბნება რომ შეცდომას უშვებთ, შედეგად

მივიღეთ გამონაკლისი. სია რომელიც იჭერს ყველა სახის ინფორმაციას არ არის უსაფრთხო სია!

მაშ ასე: სარგებელი ზოგადი ტიპიზირების:

1.     ვახდენთ ერორების აღმოჩენას Compile Time
2.     არ გვიწევს კასტინგი
3.     არ მივდივართ Run Time ერორამდე.

ყველაფერი ეს მეტყველებს იმაზე, რომ ზოგადი ტიპიზირებით კოდის წერა არის უსაფრთხო. ჯავა-ში პროგრამა ითვლება უსაფრთხოდ თუ ის ახდენს კომპილაციას ერორების და გაფრთხილებების გარეშე. იდეა ამ ყველაფრის უკან რაც დგას არის ის , რომ სწორად დაწერილი კოდი კომპილატორს აძლევს საშუალებას მოახდინოს საკმარისი გადამოწმებები ტიპებზე და შეუთავსებელი ტიპების შემთხვევაში აგვარიდოს თავიდან გაუთვალისწინებელი ერორები, როგორიც მაგალითისთვის არის ClassCastException.
                                                                                პარამეტრი
   ზოგადი ტიპის კლასი, მეთოდი თუ ინტერფეისი არის ფორმალურად ტიპიზირებული, სადაც ფორმალური არგუმენტის ადგილს იკავებს მეტობა-ნაკლებობის ნიშანში მოქცეული პარამეტრი. რომლის შეცვლაც ინსტანცირების შემდგომ ხდება რეალური პარამეტრით. ზოგადი ტიპური კლასის პარამეტრი არის რეფერენციული ტიპის დატა, რომელზეც წვდომა კომპილატორის მიერ this საკვანძო სიტყვის მეშვეობით ხდება. ჯავას აქვს საკუთარი დასახელების კონვენცია ტიპებსთვის. ნახეთ ფოტო:

სადაც კლასის ტიპად გვევლინება ნებისმიერი Capital Letter. ფოტოზე ნაჩვენებია ყველაზე ხშირად გამოყენებადი ბგერები კოდირების დროს, თუმცა ამას არსებითი მნიშვნელობა არ აქვს, თქვენ შეგიძლიათ ტიპის ადგილას ჩასვათ ის ბგერა(ან მთლიანი სიტყვა) რომელიც უფრო მართებულად მიგაჩნიათ. როდესაც კლასის გასწვრივ ვახდენთ კლასის ტიპიზირებას ფოტოზე მოცემული ბგერები იცვლება რეალური პარამეტრებით. ნახეთ მაგალითი:

ზოგადად ტიპიზირებული კლასის სინტაქსი: <ნებისმიერი რამ მოქცეული მეტობა-ნაკლებობის ნიშანში>
კლასის ინსტაცირების დროს კი ფორმალური არგუმენტის ადგილს დაიკავებს რეალური არგუმენტი.

შესაძლებელია იმდენი პარამეტრი იქონიოთ კლასის დეფინირების დროს რამდენიც გსურთ, ნახეთ მაგალითი:

მათი ერთმანეთისგან გამოყოფა ხდება სასვენი ნიშნის მეშვეობით.

 კომპილირების შემდგომ ყველა ფორმალური პარამეტრის ადგილს დაიკავებს რეალური პარამეტრი, რის შემდგომაც ინფორმაცია მივა ვირტუალურ მანქანამდე, დასამუშავებლად.

შეზღუდვები: შესაძლებელია მოახდინოთ ტიპიზირება და ინსტანცირება ნებისმიერი კლასის თუ ინტერფეისის, თქვენ მიერ შექმნილის თუ ბიბლიოთეკის მიერ შემოთავაზებულის, გარდა ენუმერაციული ტიპების, ანონიმური კლასების, გამონაკლისი კლასების და პრიმიტიული ტიპების.

თითქმის ყველა რეფერენციულ ტიპს აქვს შესაძლებლობა იყოს ზოგადდ ტიპიზირებული, როგორებიც არის, კლასი, ინტერფეისი, ჩაბუდებული კლასი, შიდა კლასი, ლოკალური კლასი, ჩაბუდებული ინტერფეისი და ა.შ.

რატომ ვერ იქნება ანონიმური კლასი ზოგადი ტიპის მატარებელი?! - ანონიმურ კლას შეუძლია მოახდინოს ტიპიზირებული ინტერფეისის იმპლემენტირება ან ტიპიზირებული კლასიდან აიღოს მემკვიდრეობით ინფორმაცია, მაგრამ თვითონ ვერ იქნება ტიპიზირებული, იმიტომ რო მას არ გააჩნია სახელი, გამომდინარე აქედან ვერ მოხდება მისი დეკლარირება და ინსტანცირება ტიპიზირებით.

რატომ ვერ იქნება გამონაკლისი კლასი ზოგადი ტიპის მატარებელი?!-
ზოგადად ტიპიზირებული კლასი პირდაპირ თუ ირიბად ვერ იქნება კონტაკტში გამონაკლისის სუპერ ინტერფეისთან  (Throwable) იმიტომ რომ გამონაკლისებზე გამკლავების მექანიზმი არის Run Time ოპერაციების ერთობა, ზოგადი ტიპიზირება კი არის მთლიანად Compile Time Type Safety პრინციპებზე აგებული. რაც ნიშნავს იმას რომ ვირტუალურ მანქანაში არ დევს ზოგადი ტიპიზირების შესახებ ინფორმაცია. ზოგად ტიპიზირებაზე ინფორმაციას კომპილატორი ფლობს, რომელიც დამუშავებულ და გადამოწმებულ კოდს აწვდის ვირტუალურ მანქანას, გამომდინარე აქედან ზოგადად ტიპიზირებული გამონაკლისის ქონა ყველა ლოგიკას მოკლებულია. (შესაძლებელია ტიპის გადასროლა throws E)

რატომ ვერ იქნება ენუმერაცია  ზოგადი ტიპის მატარებელი?!- ენუმერაცია ბუნებით არის სტატიკური, ენუმერაციული ტიპი და ენუმერაციული ღირებულებები არის სტატიკური, ვინაიდან ზოგადი ტიპიზირება შეუძლებელია ყველანაირ სტატიკურ კონტეკსტში, ენუმერაციული ტიპიზირებაც ლოგიკას მოკლებულია.

                                                                                         ბრილიანტი
არ ვიცი რატომ, მაგრამ ჯავას დეველოპერებს იზიდავს ბრილიანტი, ბრილიანტის პრობლემა გემახსოვრებათ მემკვიდრეობიდან. ტიპიზირების დროს ბრილიანტს ეძახიან მეტობა-ნაკლებობის ნიშანს სადაც არგუმენტირებული პარამეტრის ჩასმა არ ხდება, ნახეთ მაგალითი.

ჯავა 7-ის გამოსვლის შემდგომ კონსტრუკტორის მხარეს არ არის აუცილებელი მოვახდინოთ ტიპიზირება, კომპილატორი ახდენს ტიპიზირებას ჩვენ მაგივრად, ცარიელ მეტობა-ნაკლებობის ნიშანს კი პროგრამისტები ეძახიან ბრილიანტს, უფრო დეტალურად ბრილიანტს Type Inference-ზე საუბრის დროს გავეცნობით.

                                                                                          Raw Type 
  კლასი რომელიც პარამეტრების მატარებელია და რომლის ინსტანცირებაც ხდება პარამეტრების გარეშე პროგრამირებაში ნაცნობია როგორც raw type. raw type კლას გააჩნია შესაძლებლიბა იყოს მატარებელი ნებისმიერი ტიპიზირებული კლასის რეფერენციის, რომელიც შეიქმნა კონკრეტული კლასიდან. მსგავსი ოპერაცია ჯავაში ლეგალურია მაგრამ კომპილატორი გვაჩვენებს ინდიკატორს და გვეუბნება რო ხდება გადაუმოწმებელი კონვერტაცია ტიპებს შორის. ანუ კომპილატორს არ გააჩნია საკმარისი ინფორმაცია ტიპზე. ნახეთ მაგალითი:

პირველ მაგალითზე ტიპიზირებული სია სადაც არგუმენტად ინტეგრალური ღირებულება გვაქ,  მეორე მაგალითზე იგივე სია ტიპის გარეშე, მსგავსად ჩვეულებრივი კლასის ან ინტერფეისის ინსტანცირებისა, ერთი განსხვავებით raw type ტერმინი ვრცელდება ზოგადი ტიპიზირების დროს, სხვა შემთხვევებში ტერმინის გამოყენება არ არის მართებული.

  გამაფრთხილებელი მესიჯი კი ამბობს, კომპილატორი არ არის დრწმუნებული რო ინტეგრალურ ღირებულებაზე მინიჭება ხდება ინტეგრალური ღირებულების და არა რომელიმე სხვა ღირებულების. თუმცა პირიქით ობიეკტზე ინტის მინიჭება სავსებით ნორმალურია, რადგან ობიეკტი ინტის სუპერ კლასია.
კითხვა: თუ არ არის raw type კლასი ან ინტერფეისი უსაფრთხო მაშინ რატომ დაუშვეს ჯავას დეველოპერებმა მისი არსებობა? შემდეგი მაგალითი გაცემს პასუხს ამ კითხვას.

ვახდენთ ფოტოზე მოცემული კლასის ინსტანცირებას.

რის შემდგომაც ტიპიზრებული სტრინგის გადაწოდებას ვახდენთ არა ტიპიზირებულ მეთოდზე რომლის პარამეტრიც არის სია. სია ობოეკტის მატარებელია და სტრინგ ღირებულებას მარტივად ღებულობს, თუმცა დაბრუნების დროს ისევ გაფრთხილებამდე მივდივართ, რომ სმგავსი ოპერაცია არ არის უსაფრთხო, ჯავას დეველოპერებმა მსგავსი ოპერაციის ქონას შემდეგი არგუმენტით ამართლებენ, ჯერ კიდევ ჯავაში არ არის ყველა პაკეტი და კლასი ტიპიზირებული, ძირითადდ ტიპიზირება ეხება კოლექციებს, თუმცა კოლექციებს გარეთ არის უამრავი პაკეტი და ბიბლიოთეკა სადაც მეთოდი თუ კლასი არის Raw Type-ის მატარებელი, გამომდინარე აქედან, მსგავს კომპრომისზე წასვლა აუცილებელი გახდა, თუმცა მომავლისთვის ჯავა მთლიანად ტიპიზირებული წერის სტილზე გდასვლას გეგმავს, რაც Raw Type კლასების გამოყენებას აკრძალავს.

                                          ინსტანცირება და პარამეტრიზებული ტიპი
 იმისათვის რომ გამოვიყენოთ ზოგადი ტიპი, ჩვენ უნდა მოვახდინოთ წარმოება არგუმენტის, ყველა არსებული ტიპის ადგილზე. ნახეთ მაგალითი:

ფოტოზე გვაქ კლასი ზოგადი ტიპიზირებით. თუ გვსურს რო მოავხდინოთ მისი ინსტანცირება და შემდგომ გამოყენება, მოგვიწევს ფორმალური არგუმენტის ადგილას რეალური არგუმენტების შეყვანა. აგრეთვე ნებადართულია ფორმალური არგუმენტის ადგილას შევიყვანოთ სია არგუმენტებით, ანუ პარამეტრის ადგილას შევიყვანოთ პარამეტრიზებული ტიპები.


რის შემდგომაც გვაქ მარტივი მეთოდი, რომელსაც ინფორმაცია გამოაქვს ეკრანზე,

ეს იყო ინსტანცირების მაგალითი. შემდეგ მაგალითზე ვნახავთ თუ როგორ ხდება პარამეტრის პარამეტრიზება.

რაც საკმაოდ კომფორტული და მოსახერხებელია. შესაძლებელია სიის შექმნა, რომელიც დაიჭერს უამრავ სიას.


                                                                          მეთოდის ტიპიზირება
ჯავაში კლასის ტიპიზირების გარდა შესაძლებელია მეთოდის ტიპიზირებაც, სტატიკური იქნება ის თუ ინსტანციური . სინტაქსი (<Modifier> <type-variable> ) (return type) (method name) (parameter list) { ... }  გავს კლასის სინტაქს, მაგრამ აქვს გარკვეული გამოყენების წესები.  როგორც ხედავთ პარამეტრის მოთავსება ხდება return type-დე. სანამ გავეცნობოდეთ ტიპიზირებულ მეთოდს ვნახოთ პატარა მაგალითი:

ფოტოზე გვაქ მეთოდი, რომელიც თვლის იდენტურ ბგერებს მასივში, თუმცა იგივე შინაარსის მეთოდი ინტეჯერ ან ლონგ ღირებულებასთან რომ დაგვჭირდეს, მოგვიწევს მეთოდის ხელახლა დაწერა რათა მოერგოს ტიპს. ჯავა 5-ე ასეც იქცეოდნენ, ერთი და იგივე შინაარსის და სხვა და სხვა ტიპის მქონე მეთოდს წერდნენ უამრავჯერ. ან თუ იზარმაცებდით შესაძლებელი იყო ობიეკტის ტიპით გემუშავათ ყველა ტიპთან, რაც არც ისე უსაფრთხო ოპერაცია გახლდათ. ტიპიზირების შემდგომ, იგივე კოდის დაწერა შესაძლებელია ერთხელ, რის შემდგომაც გამოვიყენებთ უამრავჯერ. ნახეთ მაგალითი:

საკმაოდ კომფორტულად გამოიყურება. იმედია წარმოდგენა შეგვექმნა რა არის ტიპიზირებული მეთოდი.

წესი: მეთოდის ტიპიზირების აუცილებლობა ჩდება მაშინ როცა კლასი თვითონ არ არის ტიპიზირებული:

რომ გვქონოდა შემდეგი სურათი:

ტიპიზირება არ იქნებოდა აუცილებელი.

ტიპიზირება სტატიკურ კონტეკსტში:

როდესაც კლასი ტიპიზირებულია მეთოდის ტიპიზირება არ არის აუცილებელი თუ ის ინსტანციურია, თუმცა სტატიკური მეთოდის ტიპის გარეშე დატოვება შეუძლებელია. რადგან კომპილატორი ტიპზე წვდომას ახდენს this საკვანძო სიტყვის მეშვეობით, სტატიკურ კონტესტში კი ობიეკტს ვერ მივწვდებით თუ მასზე მიმთითებელი არ გვაქ, ჩვენ შემტხვევაში მეთოდის ტიპს. ეს ყველაფერი ხდება კადრს მიღმა, ვირტუალური მანქანა აკეთებს ყველაფერს ჩვენთვის და ჩვენც ხელი უნდა შეუწყოთ რო გამართულად იმუშაოს. ნახეთ მაგალითი:

როგორ ხდება ტიპიზირებული მეთოდის ინსტანცირება:

ტიპიზირებული მეთოდის ინსტანცირება ხდება ისევე, როგორც ჩვეულებრივი მეთოდის. მეთოდის პარამეტრიზება გამოძახების დროს შესაძლებელია ჩვენს მიერ, თუ არ მოვახდინეთ პარამეტრიზება კომპილატორი იზამს ამას ჩვენთვის. ნახეთ მაგალითი:

ორივე ოპერაცია სავსებით ლეგალურია.  მეთოდი max ეძებს ყველაზე დიდ ციფრს სიებში. ალბათ შეამჩნიეთ რო ერთი მეთოდი მუშაობს ორი სახის სიასთან, ეს იმიტომ რო მეთოდის პარამეტრად გვაქ სუპერ კლასი კოლექცია, რომელიც იღებს ყველა სახის სტრუკტურულ მონაცემს, ნახეთ ფოტოზე:

ყველაზე უცნაური რაც ამ ფოტოზე შეიძლება ნახოთ არის T extends Comparable მეთოდის ტანში, ამ ოპერაციას Bounded Type Parameter ქვია.


                                                                        პარამეტრი საზღვრებში
კლასის, ინტერფეისის და მეთოდის პარამეტრი შესაძლებელია მოექცნენ საზღრვებში, არის სიტუაცია როდესაც პროგრამისტს მსგავსი შეზღუდვის დაწესება უწევს, მსგავსი შეზღუდვა შეიძლება უამრავი მიზეზით აიხსნას, მაგალითისთვის წარმოიდგინეთ კლასი, რომელის ამოცანაც არის იმუშაოს მხოლოდ ციფრებთან, ან უფრო რეალური მაგალითი რომ ავიღოთ წარმოიდგინეთ განათლების სამინისტრო, რომლის დაქვემდებარებაშიც არის სასწავლო დაწესებულებები, მასწავლებლები და მოსწავლეები, როდესაც შევქმნით კლას უნივერსიტეტს მანდ ზედმეტი იქნება პოლიციის განყოფილება, ბანკი, თეატრი და სხვა დაწესებულებები, რომლებიც სხვა სამინისტროს განკარგულებაში არიან. ტიპის საზღვრებში მოქცევა შესაძლებელია შემდეგი სახის დეკლალირებით: < T extends something> ან <T extends something &anything &anyone> ანუ შესაძლებელია უამრავი საზღვრის დაწესებაც. თვითონ extends სიტყვა, როგორც extends ისე implements ნიშნავს, კლასთან თუ ინტერფეისთან ვიყენებთ მხოლოდ extends.  ვნახოთ მაგალითები გამოყენების წესებთან ერთად:

მაგალითისთვის შევქმნათ სამინისტრო რომელიც მიიღებს სტუდენტებს და ლეკტორებს. სკოლის და მოსწავლეების დამატებაც არ წარმოადგენს არანაირ პრობლემას, მაგრამ lets keep it simple.

ფოტოზე გვაქ თბილისის სახემწიფო უნივერსიტეტი, რომლის იმპლემენტირებას ახდენს სტუდენტი, ლეკტორი და დაცვის თანამშრომელი.
 კლასების შიგთავსი დიდად არ განსხვავებდა ერთმანეთისგან ამიტომ არ განახებთ. ამის შემდგომ კი ვქმნით სამინისტროს რომელიც მიიღებს პარამეტრად ყველა ქვე კლას თუ ინტერფეის სახემწიფო უნივერსიტეტის.

რაც ნიშნავს იმას, სამინისტროს გააჩნია დაწესებული საზღვარი რომლის ჩარჩოებშიც იმოქმედებს, მას შეუძლია მოახდინოს ინსტანცირება სახელმწიფო უნივერსიტეტის და ნებისმიერი მისი ინსტანციის. პროგრამირებაში ამ ოპერაციას Upper Bound ქვია, ტერმინს დეტალურად WildCard-ზე საუბრის დროს დაუბრუნდებით. დააკვირდით ფოტოს. უნივერსიტეტი არის Upper Bound, მის ზემდგომი ინსტანციები მხედველობაში ვერ ხვდება, ანუ უნივერსიტეტს თუ ექნება ზემდგომი ორგანო, ცენტალური ოფისი მაგალითისთვის, ან სხვა უნივერსიტეტი რომლის მფლობელობაშიც იმყოფება ვეღარ მოხვდება მხედველობის არეალში. მარტივად რომ დაიმახსოვროთ: „Upper Bound არის საზღვარი სადაც ინსტანცირება ხდება საზღვრის და ყველაფრის რაც მის ქვემოთ დგას.“

დაუბრუნდეთ მაგალითს, დეფინირების დროს კლასის ფორმალურ პარამეტრს გააჩნია წვდომა ყველა მეთოდზე იმ კლასის რომლის არეალშიც მოქმედებს. ანუ ჩევნ ფოტოზე S პარამეტრი ხედავს Tsu-ს შიგთავს, მეთოდებს, ცვლადებს, სტატიკურ მეთოდებს, და ა.შ მას არ შეუძლია მხოლოდ კონკსტრუკტორის დანახვა.

სამინისტროს კლასი:

და მისი ორი მეთოდი.

რის შემდგომაც ვახდენთ ინსტანცირებას:

და ეკრანზე გამოდის:

სამინისტროზე სხვა რამის დამატებას თუ შევეცდებით პროგრამაა რ იმუშავებს:

არ არსებობს შეზღუდვა რომელიც ხელს შეგვიშლის სამინისტროს განახლებაში, მაგალითისთვის რომ დავამატოთ სკოლები:

სკოლები შეიძლება თვითონ იყოს პარამეტრიზებული რეგიონებზე, რეგიონები რაიონებზე, რაიონები კონკრეტულ სკოლებზე დ ა.შ. იმედია ლოგიკა გასაგებია. შესაძლებელია რამოდენიმე საზღვრის ქონაც,

 ასე თუ ისე შეგვექმნა წარმოდგენა საზღვრებზე, დროა გავიხილოთ მისი გამოყენების წესები:

1.     საზღვრად შესაძლებელია ნებისმიერი ტიპის გამოყენება გარდა პრიმიტიული ტიპების და მასივების.

ფოტოზე ნაჩვენებია თითქმის ყველაფერი რაც მუშაობს საზღვრად. დააკვირდით String საზღვარს, ის ფინალური კლასია, ანუ მას მემკვიდრე არ ყავს, მისი საზღვრად ქონა კი ნიშნავს იმას რომ კლასი შეძლებს მხოლოდ String-ის ინსტანცირებას. სხვა ენით რომ დავწეროთ class Test3<String> და class Test3<T extends String> ერთი და იგივე შინაარსის მატარებელია.
2.     საზღვრად შესაძლებელია ტიპის გამოყენება.

სხვა სიტუაციები სადაც საზღვრად ტიპის გამოყენება მართებულია: ჩაბუდებული კლასი, შიდა კლასი, ლოკალური კლასი, იგივე კლასი სადაც მოხდა პარამეტრის დეფინირება, როგორც ფოტოზეა ნაჩვენები.
3.     შეუძლებელია საზღვრად ერთი და იგივე ინსტანციის სხვა და სხვა კონტექსტში გამოყენება.

4.     კლასის ფორმალურ პარამეტრს გაანია წვდომა მისი მოსაზღვრე კლასის ყველა წევრზე, გარდა კონსტრუკტორისა და დამალული წევრებისა.

ამის შემდგომ სხვა კლასიდან დავტესტოთ რაზე გააჩნია წვდომა და რაზე გვაძლევს ერორს:

როგორც ხედავთ მხოლოდ კონსტრუკტორს და private წევრს ვერ ვწვდებით.  როგორც წესი მოსაზღრვე კლასის სტატიკური წევრების გამოძახება ცუდ პრაკტიკად ითვლება.  ისევე როგორც ზემდგომი კლასის სტატიკური წევრების გადაწერა (დაჩრდილვა)
5.     შესაძლებელია მრავალ საფეხურიანი საზღვრის ქონა:


როდესაც კლას ვიყენებთ მრავალ საფეხურიან საზღვარში, ის ყოველთვის უნდა იდგეს მარცხნიდან პირველ პოზიციაზე. და მხოლოდ მის შემდგომ ინტერფეისები, თუ კლასი არ გვაქ საზღვარში მაშინ ინტერფეისების თანმიმდევრობას არ აქვს მნიშვნელობა.  იგივე წესი ვრცელდება მეთოდზეც:

ფოტოზე მაგალითისთვის, კლასი გადავიტანე მეორე პოზიციაზე, კომპილატორი გვიჩვენებს ერორს. სადაც წერია: „ამ პოზიციაზე ინტერფეისის ქონა არის მოსალოდნელი“
6.     შეუძლებელია პარამეტრისგან ობიეკტის შექმნა:
 
რადგან კომპილატორმა არ იცის რა ტიპის მატარებელია პარამეტრი, შესაბამისად უცნობი ტიპისგან ობიეკტის შექმნა ლოგიკას მოკლებულია. რეფლექციას თუ გამოიყენებთ ირიბად შეძლებთ ინსტანცირებას. თუ გაინტერესებთ რა არის რეფლექცია მოიძიეთ მასალა.
7.     იგივე ლოგიკით შეუძლებელია პარამეტრის მასივის სახით ინსტანცირება. T[] array = new T[10] // error
8.     შესაძლებელია პარამეტრის კასტინგი მაგრამ კომპილატორი გიჩვენებთ გამაფრთხილებელ მესიჯს.
9. შესაძლებელია პარამეტრის გადასროლა. Throws T // legal სხვა დანარჩენ სიტუაციებში გამონაკლისთან კავშირი არ არის ლეგალური.
10.შეუძლებელია მემკვიდრეობით ინფორმაციიის აღება ტიპისგან. Class SomeClass extends T // error. ტიპი უცნობია.
11.                        ტიპიზირებული პარამეტრი და სტატიკური კონტეკსტი.

ნებისმიერი სტატიკურ კონტეკსტში გვაქ ერორ მესიჯი, რომელიც ამბობს. შეუძლებელია სტატიკურ კონტეკსტში კომპილატორი მიწვდეს რეფერენციულ ცვლადს. თუ სწორად მახსოვს მაღლა დავწერე რო ტიპი არის რეფერენციული ტიპის ცვლადი და კომპილატორი მასზე წვდომა ახდენს this საკვანძო სიტყვის მეშვეობით. მემკვიდრეობიდან გემახსოვრებათ რო this ობიეკტზე მიმთითებელია, რომლის გამოყენებაც არ შეიძლება სტატიკურ კონტეკსტში. რადგან სტატიკური წევრი კლასის დონის წევრია და მისგან ობიეკტი არ იქმნება.  იგივე წესი გავრცელდება ინტერფეისზეც, ალბათ გახსოვთ რო ნებისმიერი რამ რისი დეფინირებაც ხდება ინტერფეისში სტანდარტულად არის public abstract მოდიფიკატორით, ველები public static final მოდიფიკატორით.  ჩაბუდებული კლასიც კი ინტერფეისში არის სტატიკური, მიუხედავად იმისა რომ ის გამოიყურება როგორც შიდა კლასი.  ნახეთ მაგალითი:


                                          ტიპიზირებული კლასები  და მემკვიდრეობა
მემკვიდრეობა ტიპიზირებულ კლასებს  შორის მცირედით განსხვავდება იმ მემკვიდრეობისგან რომელსაც ჩვენ ვართ მიჩვეული, წარმოიდგინეთ:

ფოტოზე მოცემული სიტუაცია, სადაც ახალი არაფერია, ყველამ ვიცით რომ double-ის სუპერ კლასია ობიეკტი, მემკვიდრეობის წესებით კი სუპერ კლას აქვს იმის შესაძლებლობა რო ქვე კლასი დაიჭიროს, პოლიმორფიზმის, მემკვიდრეობის, ენკაპსულაციის თუ აბსტრაქციის სიტუაციაში ოპერაცია სრულიად ლეგალურია.  იგივე ლეგალურია შემდეგ სიტუაციაშიც:

მაგრამ არ არის ლეგალური შემდეგ სიტუაციაში:

რა გამოდის, Number-ის მაგალიზე ოპერაცია სწორია და ბოლო ფოტოს მაგალითზე არა? ან როგორ შეიძლება რო ობიეკტმა ვერ დაიჭიროს სხვა ნებისმეირი ცვლადი? ყველა წესი რაც გვისწავლია ირღვევა ამ პატარა ფოტოზე, მემკვიდრეობა, პოლიმორფიზმი, ენკაფსულაცია, ყველა ფუნდამენტური პრინციპები რაზეც ჯავა დგას მოულოდნელად უმოქმედო გახდა. რატომ?

დავიწყოთ Number-ის მაგალითიდან, როგორც ესეთი ვირტუალ მანქანაში არ არსებობს ცნება ზოგადი ტიპიზირება, ეგ ცნება არსებობს კომპილატორისთვის და რას ნიშნავს ეს?! კომპილატორი ვირტუალურ მანქანას აწვდის კონკრეტულ ინფორმაციას და არა ზოგად ტიპს, როდესაც ჩვენ ვამბობთ რო სია იღებს Number ღირებულებას ის ვირტუალურ მანქანაში ხვდება როგორც კლასი Number და არა როგორც კლასები Number, Double, Long და ა.შ.  მიუხედავად ამისა მემკვიდრეობა ხომ მაინც უნდა ფუნქციონირებდეს? ზოგადი ტიპი თუ არ არსებობს ვირტუალ მანქანაში მემკვიდრეობა ხომ არსებობს? კი მემკვიდრეობა არსებობს მაგრამ კომპილატორი არ ეუბნება ვირტუალურ მანქანას რომ ამა თუ იმ კლას მემკვიდრე ამ მშობელი ყავს, ის ეუბნება რომ მხოლოდ ამ კლასთან იმუშავებს და ვირტუალური მანქანაც მორჩილად მიზდევს მითითებებს, კარგი- გასაგებია რაც ხდება მაგრამ როგორ გავაგებინოთ ვირტუალურ მანქანას რომ ამა თუ იმ კლას მემკვიდრე ყავს? ამისთვის ზოგად ტიპიზირებაში შემუშავებულია საზღვრები და მასზე ცოტა ხანში ვისაუბრებთ. დაუბრუნდეთ ფოტოს სადაც ერორ მესიჯები გვაქ, ერორ მესიჯები იმიტომ გვაქ რომ, type safety პრინციპებზე დაყრდნობით არ არის მართებული ერთი სიაში ვიქონიათ სხვა და სხვა ტიპის ობიეკტები, როდესაც სიის ეგზეკუციას მოვახდენთ როგორ უნდა გაერკვეს ვირტუალური მანქანა რომელ ტიპთან იმუშაოს? სტრინგთან, ინტთან თუ ობიეკთან? ნახეთ მაგალითი:

რომც მოგვცეს მსგავსი ოპერაციის წარმოების საშუალება ჯავას დეველოპერებმა, ჩვენ დაუშვებთ შეცდომას, იმიტომ რო ფიზიკურად შეუძლებელია დაიმახსოვრო მილიონიან სიაში ტიპების თანმიმდევრობა. რეალური ცხოვრების მაგალითზე რო განვიხილოთ ეს სიტუაცია: წარმოიდგინეთ მუშაობთ კომპანიაში, ხართ პროგრამისტი, ბუღალტერმა კი ფინანსისტი, იურისტი, დამლაგებელი და დაცვის თანამშრომელი ერთ სიაში დაგიმატათ, მოსახერხებელი იქნება ვინმესთვის მსგავსი სიის ქონა? თუ უფრო კომფორტული იქნება რომ იქონიოთ ფინანსისტთა სია, იურისტთა სია ცალ-ცალკე?

მემკვიდრეობის დროს კლასი ან ინტერფეისი რომელიც იღებს პარამეტრს აწოდებს მას მის ზემდგომ კლასებს და ინტერფეისებს, თუ მიიღო String , ArrayList-მა, List და Collection-ც გახდბიან String ტიპის მატარებლები, მემკვიდრეობას რომ მივაღწიოთ ტიპიზირების დროს ხშირად საკამრისია მემკვიდრეობის წესებს მივყვეთ და მოვახდინოთ კლასების მემკვიდრეობით გადაცემა და ინტერფეისების მემკვიდრეობით აღება, ნახეტ ფოტო ჯავას ოფიციალური ვებ გვერდიდან. :
                           
დასკვნა:

სტრინგი არის ობიეკტი მემკვიდრეობის დროს მაგრამ არა ტიპიზირებული ოპერაციების დროს, არაფერი საერთო არ აქვს List<String> და List<Object>-ს.

                                                                            Type Inference
Type Inference არის პროცესი, როდესაც კომპილატორი არგუმენტის ავტომატურ რეჟიმში დადგენას ახდენს,  არგუმენტი კლასის იქნება თუ მეთოდის არ აქვს მნიშვნელობა. ამ პროცესის დროს არგუმენტის ხელოვნურად დეფინირება არ წარმოადგენს აუცილებლობას. ძირითადად არგუმენტის დადგენა 2 სცენარის დროს გვხვდება. 1. როდესაც ზოგადი ტიპიზსგან ობიეკტი იქმენა. 2. როდესაც ვიძახებთ ზოგადად ტიპიზირებულ მეთოდს.

როგორც წესი თქვენ ხელოვნურად უნდა მოახდინოთ ტიპის არგუმენტირება, მაგრამ ჯავა 7-ის გამოსვლის შემდგომ ამის აუცილებლობა არ არსებობს, კომპილატორი ჩვენ მაგივრად უყურებს მარცხნივ მდგომ არგუმენტს დ ახდენს მარჯვნივ მდგომი ტიპის ადგილას მის ჩასმას. ცარიელ მეტობა ნაკლებობის ნიშნავს კი ბრლიანტის ნიშანს ეძახიან.

მეთოდის დამუშავება კომპილატორის მიერ იგივე პრინციპიტ ხდება.

კომპილატორი ადგენს რო ინფორმაცია რომელსაც სია ატარებს არის ინტეგრალური, ფორმალური ტიპი ამ შემთხვევაში იცვლება რეალური არგუმენტით და მეთოდი თვითონ ხდება ინტეგრალური.  მსგავსი ლოგიკით T შეიცვლება მთლიან იერარქიაში, არა მარტო მოცემულ კლასში არამედ, List Interface, Collection Interface და თქვენ მიერ დამატებული ოპერაციები რომლებიც T ტიპის მატარებლები იქნებიან, შეიცვლებიან ინტეგრალური არგუმენტით.

                                                                             WildCard

არსებობს სამი სახის WildCard:

1.     Unbounded WildCard
2.     Upper Bounded WildCard
3.     Lower Bounded WildCard

სანამ სახეობებს განვიხილავდეთ, დავიამხსოვროთ ერთი წესი,  უცნობი ტიპის გამოყენება არ ხდება კლასის დონეზე, შეუძლებელია კლასმა მიიღოს და დაამუშაოს უცნობი პარამეტრი, ლოგიკურადაც კი არ ჟღერს. ისევე როგორც მეთოდის.

იგივე ლოგიკით თუ ვიმსჯელებთ, კლასი იმიტომ არ იღებს უცნობ ტიპს პარამეტრად რო ვერ ახერხებს მის ინსტანცირებას, გამომდინარე აქედან მისი ინსტანცირება შეუძლებელია ყველგან,  დაბლა ფოტოზე რასაც ნახავთ იგივეა რაც მაღლა ფოტოზე ნახეთ:

სხვა სიტყვებით, რომ ვთქვათ: წარმოვიდგინოთ რომ შესაძლებელია უცნობი ტიპის ინსტანცირება, უცნობო ტიპი ნიშნავს ყველაფერს. უცნობი = ყველაფერს, გამოვა რო კლასი ყველაფრის ინსტანცირების საშუალებას მოგვცემს რაც ზოგადი ტიპიზირების ფუნდამენტურ წეს Type Safety-ს პრინციპებს არღვევს. სინამდვილეში, კომპილატორისთვის <?> გამოიყურება <? Extends Object > დაუბრუნდეთ მაგალითებს:

Unbounded WildCard - მეთოდის პარამეტრად: მას აქვს უნარი მიიღოს და დაამუშაოს ნებისმიერი სახის ინფორმაცია რადგან, ტიპი უცნობია, ნახეთ შემდეგი მაგალითი:

მაგრამ იგივე უნარი ხო უბრალოდ <T> ოპერაციასაც აქვს?

ორივე მეთოდი არსულებს ერთი და იგივე ოპერაციას, ერთი და იგივე შედეგით. რაშია სხვაობა?
1.     List<?> კომპილატორს ეუბნება რო სია იღებს უცნობ ინსტანციებს, რომელიც არ ვიცით.
2.     List<T> სია იღებს ინსტანციებს T ტიპის.
კი , გასაგებია. მაგრამ T-ც ხომ უცნობია რა მოხვდება მის ადგილზე არ ვიცით, ეგეც მართალია მაგრამ ვიცით რა დაბრუნდება მისი ადგილიდან, განსხვავებით List<?> სადაც ? არის ობიეკტის რეფერენცია და ის ყოველთვის შეეცდება დააბრუნოს ობიეკტი, ეს არის მათ შორის სხვაობა T დააბრუნებს კონკრეტულ ტიპს, ?- დააბრუნებს ობიეკტს, ამას მაშინ ხვდები როცა ცდილობ დაამატო სიას რამე. ნახეთ მაგალითი:

თუ ასეთი მოუქნელია, მაშინ რატო უნდა გამოვიყენოთ საერთოდ?

Unbounded WildCard-ის პლიუსები:

1.     პროგრამისტს ეძლევა საშუალება ნაკლები იფიქროს იმაზე თუ რა ობიეკტი იმყოფება სიაში.
2.    თუ სტანდარტულად გადავაწოდებთ ობიეკტს მაშინ მომხმარებელს ნაკლები ინფორმაცია აქვს იმაზე თუ როგორ და რა სახით მოხდა მეთოდზე ობიეკტის გადაწოდება, რაც თავის მხრივ კიდევ ერთ დამატებით პიუს გვაძლევს, მომხარებელმა არ იცის როგორც გამოიყენოს ეს ინფორმაცია, ანუ უწესებთ ლიმიტებს.  („ხედავს მაგრამ არ იცის რა არის)
3.     შეუძლია მოახდინოს ნებისმიერი ტიპის ინსტანცირება , მიუხედავად იმისა რო Default Type Object აქვს, შემდეგ მაგალითზე ნახეთ:

პირველი ველი მუშაობს მაგრამ მეორეზე კომპილატორი ერორ მესიჯს გვიჩვენებს. მიუხედავად იმისა რო პირველი მუშაობს მას ოპერაციების დიდი არჩევანი არ აქვს, როგორც მაღლა ვნახეთ ვერაფერს დავამატებთ მსგავს სიას.
4.     მისი ძირითადი კონკურენტი არის Raw Type, კონკურენტი უხეში ნათქვამია მაგრამ როდესაც <?> ვლაპარაკობთ მას ვადარებთ ტიპიზირებული კლასის სტანდარტულ ინსტანციას, თუ დავფიქრდებით სტანდარტულ ინსტანციას ყველაფრით ჯობია, ანუ თუ სურვილი გვაქვს გამოვიყენოთ სტანდარტული ინსტანცია უკეთესი იქნება თუ გამოვიყენებთ <?> უცნობ ტიპს.
Raw Type არ არის უსაფრთხო, <?> უსაფრთხოა, Raw Type-ს ყველა სიასთან ცალ-ცალკე უწევს მუშაობა <?> ერთი მეთოდით შეუძლია დაადგინოს სიდიდე ნებისმიერი სიის ან წაშალოს ნებისმიერი სიის შიგთავსი.


Upper Bounder WildCard - მისი დეკლალირება ხდება მსგავსად <T extends Object>, უბრალოდ T-ს ადგილას ? უნდა დავსვათ. გამოყენების წესებიც იგივე აქვს, ერთი განსხვავებით, შეუძლებელია მისი დეკლალირება კლასის ან მეთოდის ტანში. Class<? Extends something>, public <? Extdens something> void … რადგან არც კლასმა და არც მეთოდმა არ იციან რასთან უწევთ მუშაობა.
მაღლა ვისაუბრეთ რომ List<String> არ არის List<Object>, რადგან ვირტუალურ მანქანას ზუსტად ჭირდება ცოდნა თუ რასთან აქვს საქმე, Upper Bounder WildCard შემთხვევაში თუ რამოდენიმე ნათესაური კლასი ან ინტერფეისი გვაქვს რომლის საძღრვებშიც ვმუშაობთ მაშინ მემკვიდრეობის წესებიც ჩვეულებრივად ვრცელდება მონიშნულ კლასებზე. ნახეთ მაგალითი:

Number არის  int, double, bigD და ა.შ.  (მიუხედავად იმისა რო მსგავს სიას ევრაფერს დავამატებთ, კომპილატორი ერორ მესიჯს არ გვიჩვენებს)
უფრო რეალურ მაგალითზე.. წარმოიდგინეთ ცხოველთა ინტერფეისი, რომელის იმპლემენტირებასაც ახდენენ ცხოველები, ძაღლი, კატა და ა.შ. ჩვენ კი ვცდილობთ მათ ეკრანზე გამოტანას ერთდროულად, შემდეგი მეთოდით.

თუმცა..

როგორ მაღლა ვისაუბრეთ, ცხოველი არ არის ბულდოგი ტიპიზირების დროს, გამომდინარე აქედან ვერ ხერხდება ბულდოგის კომპილირება, როგორ ვამცნობოთ კომპილატორის რო ბულდოგი ცხოველის ტიპის არის?

იგივე მეთოდში, ვეუბნებით რო მეთოდი მიიღებს არგუმენტად ნებისმიერ ობიეკტს რომელიც იქნება ცხოველის ქვე-კლასი ან ინტერფეისი, ცხოველის ჩათვლით. ალბათ გიჩდება კითხვა, <T extends Animal> და <? extends Animal> შორის რა სხვაობა.
1.     <T extends Animal> აბრუნებს კონკრეტულ ტიპს, <? extends Animal> აბრუნებს ობიეკტს.
2.     WildCard-ს შეუძლია ექნეს ერთი საზღვარი, <? Extdends T>, ტიპურ პარამეტრს შეუძლია ექნეს უამრავი საზღვარი. <T extends A&B&C&D>.
3.     T-ს შემთხვევაში თქვენ გიწევთ გამკლავება მის ტიპებთან მეთოდებთან და ა.შ მაგრამ <?> შემთხვევაში თქვენ არაფერზე არ გიწევთ გამკლავება, ეს ფიზიკურად შეუძლებელია.
4.     <?> თქვენ გაქვთ შესაძლბელობა მხოლოდ წაიკთხოთ ინფორმაცია სიიდან. T-ს შემთხვევაში თქვენ გაქვთ შესაძლბელობა წაიკითხოთ და შეავსოთ სია. (სიტუაციას გააჩნია) მოახდინოთ მისი მოდიფიცირება, შეცვლა, გადაწერა და ა.შ
5.     <?extends T> გამოყენებით კოდი უფრო მოქნილი და პოლიმორფიული ხდება.


Lower bounder wildcard - მსგავსად Upper Bound-სა ტიპიზირება შეიძლება Lower bound-აც,  თუმცა ეს არ ნიშნავს იმას რომ მათი ქონა ერთდროულად შეიძლება, ან თუნდაც იქიონიოთ ერთზე მეტი ტიპი საზღრვებში მოსაქცევად, ანუ, <?extends Number super Integer> არ არის მართებული. სინტაქსი ქვედა საზღვრის არის <? Super T> სადაც T ბოლო კლასია იერარქიაში, Super კი ნიშნავს იმას რომ პარამეტრი, ცვლადი თუ ველი მიიღებს ყველაფერს რაც სუპერკლასია კონკრეტული ობიეკტის.  ყველაზე ხშირი გამოყენება მას აქვს, როცა ამა თუ იმ სიას ვამატებთ ელემენტს. როგორც მაღლა ავღნიშნე Upper Bound-ის დროს სიას ვერაფერს დაამატებთ, ის გამოიყენება მაშინ როცა სიიდან რამის წაკითხვა გვინდა,  Lower bounde კი გამოიყენება მაშინ როცა სიაში რამის დამატება გვინდა. წარმოიდგინეთ შემდეგი სიტუაცია:

გვაქ მეთოდი, რომელიც ამატებს სიას double ღირებულებებს, მაგრამ როგორ შეიძლება მეთოდი გავხადოთ უფრო მოქნილი, რო იმუშაის გარკვეულის სახის ტიპებთან ერთდროულად. დაუშვათ გვსურს რო დავაამტოთ Number, მაშინ ვიქცევით შემდეგნაირად:

<? Super Integer>-ის მეშვეობით კომპილატორს ვეუბნებით რო ეს მეთოდი მიიღებს ყველა სახის კოლექციას, (როგორც ფოტოზე ხედავთ არაილისტი და ლინკდლისტი გვაქ) და დაამუშავებს ყველაფერს რაც სუპერკლასია Integer კლასის, ინტ კლასის სუპერ კლასია Number და Object, მაგრამ ფოტოზე ჩანს რო Number კლასმა double ღირებულებაც დაიმატა, ეგ როგორ? იერარქიაში არ არის double, Integer>Number>Object ასე გამოიყურება იერარქია,  double ღირებულება იმიტომ დაემატა რომ Number კლას აქვს შესაძლბელობა მიიღოს ყველა სახის ციფრი. თუმცა ჩვენ რომ ვცადოთ კონკრეტულად double ღირებულების დამატება და არა პირობითად Number ტიპის, კომპილატორი ამის უფლებას აღარ მოგვცემს, იმიტომ რომ ის გაერკვევა რო ციფრი არის double და არა Number, ნახეთ მაგალითი:

თუმცა მეთოდის ტანში.

სიას ვამატებთ ობიეკტსაც.  კომპილირების შემდგომ:


და მოულოდნელად, მოგვცეა საშუალება სტრინგთან ვიმუშაოთ:

მაგრამ ნუღარ იზამთ ამას, არ არის კარგი პრაკტიკა და რატო Type Erasure-ზე საუბრის დროს მიხვდებით. (კოდი კომპილირდება ჩვეულებრივად) იმედია იდე super Wildcard-ის გასაგებია.

ასე თუ ისე წარმოდგენა შეგვექმნა რა არის ზოგადი ტიპიზირებული ტიპი, საზღვრები, მაღალი და დაბალი საზღვრები, გამოიყენეთ ისინი მაშინ როცა ხედავთ ამის აუცილებლობას, როდის არის ამის აუცილებლობა? ჯოშუა ბლოხს თუ დაუჯერებთ, Upper Bound Wildcard - გამოიყენეთ მაშინ როცა სია, ან კლასი აწარმოებს ინფორმაციას, (მხოლოდ წაკითხვის მიზნით) Lower Bound Wildcard - გამოიყენეთ მაშინ როცა სიაში შეგყავთ ინფორმაცია და არასდროს არ გამოიყენოთ ისინი თუ სია აწარმოებს და იღებს ინფორმაციას ერთდროულად. მესამე შემთხვევაში გამოიყენეთ ჩვეულებრივი მეთოდი ან ნებისმიერი ოპერაცია.
1.     თქვენ ვერაფერს ვერ დაამატებთ სიას <? Extends T> გარდა null-სა.
2.     თქვენ ვერაფერს ვერ გამოიტანთ სიიდან <? Super T> გარდა Object-სა.
მაღლა ვახსენე რო List<String> და List<Object> არაფერი აქვთ საერთო ერთმანეთან, თუმცა WildCard-ის გამოყენების შემთხვევაში ჩვენ გვაქ შესაძლბელობა შევქმნათ უერთიერთობა კლასებს შორის, როგორ lower ისე upper WildCard-ის შემთხვევაში, ვნახოთ რამოდენიმე მაგალითი:

ახსნა განმარტებას არ საჭიროებს.

                                                                         WildCard Capture
როგორც ვიცით <?> არის უცნობი ტიპი, (სტანდარტულად ობიეკტი), მაგრამ ეს ინფორმაცია არ არის საკმარისი ვირტუალური მანქანისთვის, კომპილატორი ხომ ვირტუალურ მანქანას კონკრეტულ ტიპებს აწვდის და არა ზოგად ტიპს, ამიტომ არის შემთხვევები როცა ჩვენ გვიწევს დავეხმაროთ კომპილატორს ტიპის დადგენაში, რათა ვირტუალურმა მანქანამ იმუშაოს გამართულად, ამ პროცეს პროგრამირებაში Capturing ქვია, ანუ ჩვენი დახმარებით კომპილატორი ერკვევა რა ტიპთან მუშაობს, და Type Erasure-ის შემდგომ გამართულ კოდს აწვდის ვირტუალურ მანქანას კომპილირებისთვის. წარმოიდგინეთ შემდეგი სიტუაცია:

გვაქ მეთოდი, რომელიც იღებს 2 სიას და ინდექს, მეთოდის მიზანია, მითითებული ინდექსიდან პირველ სიაში გადაიტანოს იგივე ინდექსზე მყოფი ციფრი მეორე სიიდან, საკმაოდ პრიმიტიული მეთოდია, მსგავსი მეთოდები როგორც წესი IndexOutOfBounds გამონაკლის ისვრიან მაგრამ არ დავკონკრეტდეთ, მარტივ მაგალითს მივყვეთ. როგორ ფიქრობთ რატოა list2.get(i) გაწითლებული?, იმიტომ რო არც ვირტუალურმა მანქანამ და არც კომპილატორმა არ იციან რა ტიპის არის მეთოდები, ჩვენ ვეუბნებით რო არიან ზოგადი ტიპის მაგრამ ზოგადი ტიპიდან ინფორმაციის გამოტანა არ არის უსაფრთხო არც დამატება, ამიტომ კომპილატორი ისვრის გამონაკლის. რომელიც ამბობს, თქვენ ცდილობთ int ინდექსზე დაამუშაოთ ობიეკტი, კომპილატორი ამ მესიჯით ცდილობს რო გვითხრას უფრო უსაფრთხო კოდი მჭირდება დასამუშავებლად, რადგან ობიეკტის ტიპში მოულოდნელად შეიძლება ინტ, სტრინგ, დაბლ და ნებისმიერი სხვა ტიპი აღმოჩდეს რაც ზოგადი ტიპიზირების ფუნდამენტურ წესებს ეწინააღმდეგება. იგივე მაგალითი გავხადოთ უფრო უსაფრთხო და ტიპიზირებული. ამისთვის გამოვიყენებთ დამხმარე მეთოდს, რომელიც მოახდენს Type Capture-ს.

თუმცა როგორც ხედავთ არც მსგავს ოპერაცია არის კომპილატორისთვის უსაფრთხო. მიზეზი ალბათ ნათელია, კომპილატორისთვის ისევ გაურკვეველია თუ რა სახის ტიპს გადავაწოდებთ მეთოდს, დამხმარე მეთოდში ჩვენ ვეუბნებით რომ გადავაწოდებთ T ტიპის ობიეკტს, ანუ თუ გადავაწოდებთ Integers მეთოდი თავისი სიებით გახდება Integers მატარებელი. მაგრამ არც ის არის გამორიცხული რომ გადავაწოდოთ Integers და Double, სწორედ ამიტომ არ აქვს გარანტია იმისა რომ სხვა და სხვა ტიპების გადაწოდება მოხდება მეთოდზე.  ვნახოთ კოდი ჩასწორების შემდგომ:

ინტის გარდა ნებისმიერი სხვა ტიპი რო გადავაწოდოთ კომპილირება არ მოხდება, ფოტოზე მოცემული კოდი არის ცუდათ დაწერილი კოდი, მაგრამ მაგალითისთვის გამოდგება, (იგივე სიტუაციაში Upper-Lower WildCard-ის გამოყენება იქნება კარგი პრაკტიკა) რომ ვნახოთ როგორ ახდენს კომპილატორი Type Capturing-ს. უფრო პრიმიტიული მაგალითზე:

ეს მეთოდები კი დაიჭერენ ნებისმიერ ტიპს. პირველი მეთოდი მოახდენს Type Capture-ს მეორე მეთოდის დახმარებით. იგივე პრინციპით შეგიძლიათ მაოხდინოთ Type Capture Upper and Lower Bounded WildCard-ის შემთხვევაშიც. ნახეთ მაგალითი:

კარგ პრაკტიკად ითვლება Bounded Wildcard-ს თუ არ გამოიყენებთ return type მეთოდებში. შეხვდებით, შეიძლება მოგიწიოთ კიდეც მსგავსი კოდის დაწერა მაგრამ ზოგადად რაც უფრო ნაკლებად იზამთ მსგავს რამეს მით უკეთესი.

თუ შეამჩნიეთ Lower Bound არ აქვს კლას, მსგავსი ცნება ჯავაში არ არსებობს>>> public class SomeClass<T super Integer>.

Wildcard შეუძლია ექნეს შემდეგი ტიპის საზღვრები:

დაკვირდით some5 ცვლადს, საზღვრად პრიმიტიული ტიპის მასივი გვაქ. რაც ფორმალური პარამეტრის შემთხვევაში შეუძლებელია.


                                                                              Type Erasure
მაღლა რამოდენიმეჯერ ვახსენე რო კომპილატორი ვირტუალურ მანქანას დასამუშავებლად აწვდის კონკრეტულ ტიპს. ამ თავში ვისაუბრებთ თუ როგორ ხდება ეს მიწოდება,
1.     კომპილატორი ახდენს ჩანაცვლებას ტიპური პარამეტრის ობიეკტით ან/და მისი Upper საზღვრის პირველი ინსტანციით.  თუ პარამეტრი არ არის საზღვრებში მაშინ მას ცვლის ობიეკტით თუ ის საზღვრებშია პირველი კლასით ან ინტერფეისით.
2.     ტიპი რომელის შეგვყავს ტიპიზირებულ ველში ექცევა კასტინგის ქვეშ კომპილატორის მიერ თუ ამის აუცილებლობა არსებობს
3.     ახდენს დამაკავშირებელი მეთოდების გენერირებას რომ პოლიმორფიზმის პრინციპები არ დაირღვეს.
სულ ეს არის, ამ სამი პუნკტით ხელმძღვანელობს. ვნახოთ მაგალითები:

თუ კლასი უბრალოდ ტიპიზირებულია:
ვირტუალურ მანქანას მიდის შემდეგი ინფორმაცია:

ყველა ტიპის ადგილას კომპიალტორმა მოათავსა ობიეკტი.

დაახლოებით იგივე ხდება როცა ტიპიზირება საზღვრებში გვაქ.

ვირტუალურთან დასამუშავებლად მიდის შემდეგი კოდი.

დააკვირდით კარგად ფოტოს, ვირტალურ მანქანს მიდის პირველი კლასი ან ინტერფეისი რომელიც საზღვართან ახლოს დგას, ანუ ხელ-მარცხნივ მყოფი კლასი ან ინტერფეისი, ის არასდროს უყურებს რა დგას ხელ მარჯვნივ ან რამდენი დგას. ამუშავებს მხოლოდ პირველ მოსაზღვრე კლას ან ინტერფეის. საზღვრებში მყოფი დანარჩენი ინფორმაცია ექცევა ობიეკტიდან კასტინგის ქვეშ და ხდება კონკრეტული ტიპი. იგივე პრინციპით ხდება კომპილატორზე ინფორმაციის მიწოდება მეთოდის შემთხვევაშიც:

შეიძლება რამე გამომრჩა კიდეც მაგრამ ვირტუალურ მანქანამდე დაახლოებით მსგავსი კოდი მიდის დასამუშავებლად. ორივე მეთოდი მუშაობს ნებისმიერი ტიპის სტრუკტურულ მონაცემთან და ახდენს სუმირებას ნებისმიერი ტიპის ინტ ტიპად.
 ზოგჯერ ვირტუალურ მანქანაზე კოდის მიწოდებას აქვს გაუთვალისწინებელი შედეგები, (სიტუაცია რომლის გათვალისწინებაც პროგრამისტმა ვერ მოხერხა), ასეთ სიტუაციაში კომპილატორი ქმნის სინთეთიკურ მეთოდს, პროგრამულ ენაზე მსგავს მეთოდს Bridge Method-ს ეძახიან, შემდეგ მაგალითზე ნახეთ:

თუ ტიპიზირებული კლასიდან მემვკდირებით ინფორმაციას იღებს ჩვეულებრივი კლასი:

მაშინ კომპილატორს უხდება დამხარე მეთოდის შექმნა, რათა მემკვიდრეობამ გამართულად იმუშაოს, დამხმარე მეთოდი ფოტოზე შავ ჩარჩოებშია მოქცეულია, ზუსტად მსგავს მეთოდს ქმნის კადრს მიღმა კომპილატორი. კლასის კონვერტირების შემდგომ ვიცით რომ კომპილატორი ტიპებს ცვლის ობიეკტით, თუმცა მსგავსი კლასის იმპლემენტირების დროს ობიეკტი არ მოდის თავსებადობაში Double-ან, დამხმარე მეთოდის საშუალებით ამ პრობლემაზე ფიქრი არ გვიწევს, საერთოდ მსგავსი ინფორმაციის ცოდნაც არაფერს მოგცემთ, მაგრამ ჯობია რამე მოგცეთ ვიდრე არაფერი. 

 შეუძლებელია მეთოდების გადატვირთვა თუ Type Erasure-ის შემდგომ ისინი ერთი და იგივე ტიპის მაატრებლები გახდებიან.

                                                                       Heap Pollution
  Heap Pollution არის პროცესი, როდესაც არგუმენტირებული სიის ცვლადი მიგვითითებს ობიეკტზე, რომლის ტიპიც შეუთავსებელია პარამეტრისთვის.  მისი გამოწვევა ხდება 3 შემთხვევაში:
1.     Raw type  ტიპის და პარამეტრიზებული ტიპის არევის დროს
2.     შეუმოწმებელი და უსაფრთხო კასტინგის დროს
3.     შეუთავსებელი ოპერაციების დროს.
ნახეთ ფოტო

პირველ მაგლითზე გაფრთხილებას იმიტომ იძლება მეორეზე კი Heap Pollution-ს გარდა იმისა რო ოპერაციები არ არის უსაფრთხო, არის შეუთავსებელიც. როდესაც არ არის უსაფრთხო გაფრთხილებას იძლევა კომპილატორი, როდესაც შეუთავსებელია მიდის Heap Pollution-ე. მესამე მაგალითს არ გაჩვენებთ, ფაილებს ეხება. ამიტომ ფაილებზე საუბრის დროს დაუბრუნდები. ამით ზოგად ტიპიზირებულ ტიპებზე საუბარი მორჩა. იმედია წარმოდგენა შეგექმათ რა არის ტიპი, არგუმენტი და საზღვრები. არის უამრავი დეტალი რომელიც გამოვტოვე, რადგან პრაკტიკაში დეტალებს თვითონ გაეცნობით. გისურვებთ წარმატებებს.








Comments

Popular posts from this blog

ალგორითმები

სტრუკტურული მონაცემები. (სია - List)

სტრუკტურული მონაცემები - (რიგი - Queue)