Жалпылоо Generic’тер
Генерик же генериктер (генерикалык типтер жана методдор) колдонулган типтердин катаал аныктамасынан алыс болууга мүмкүндүк берет. Алар бизге керек болушу мүмкүн болгон көйгөйдү карап көрөлү.
Банк эсебин көрсөтүү үчүн классты аныктайлы дейли. Мисалы, мындай көрүнүшү мүмкүн:
class Account{
private int id;
private int sum;
Account(int id, int sum){
this.id = id;
this.sum = sum;
}
public int getId() { return id; }
public int getSum() { return sum; }
public void setSum(int sum) { this.sum = sum; }
}
Эсеп классында эки талаа бар: id - эсептин уникалдуу идентификатору жана сумма - эсептеги сумма.
Бул учурда идентификатор бүтүн сан катары берилет, мисалы 1, 2, 3, 4 ж.б. Бирок, сап баалуулуктары да көбүнчө идентификатор үчүн колдонулат. Сандык жана саптык маанилердин да жакшы жана жаман жактары бар. Ал эми классты жазып жаткан учурда, идентификаторду - сапты же санды сактоо үчүн эмнени тандоо жакшыраак экенин так билбей калышыбыз мүмкүн. Же, балким, бул класс бул маселе боюнча пикири бар башка иштеп чыгуучулар тарабынан колдонулат. Мисалы, алар id түрү катары өз класстарын колдонгусу келет.
Бир караганда, биз бул маселени төмөнкүчө чече алабыз: id түрүн Объект түрүндөгү талаа катары белгилеңиз, ал бардык башка типтер үчүн жалпы жана негизги суперкласс болуп саналат:
public class Program{
public static void main(String[] args) {
Account acc1 = new Account(2334, 5000); // id - число
int acc1Id = (int)acc1.getId();
System.out.println(acc1Id);
Account acc2 = new Account("sid5523", 5000); // id - строка
System.out.println(acc2.getId());
}
}
class Account{
private Object id;
private int sum;
Account(Object id, int sum){
this.id = id;
this.sum = sum;
}
public Object getId() { return id; }
public int getSum() { return sum; }
public void setSum(int sum) { this.sum = sum; }
}
Бул учурда, баары сонун иштейт. Бирок, анда биз типтеги коопсуздук көйгөйүнө туш болобуз. Мисалы, төмөнкү учурда, биз ката алабыз:
Account acc1 = new Account("2345", 5000);
int acc1Id = (int)acc1.getId(); // java.lang.ClassCastException
System.out.println(acc1Id);
Көйгөй жасалма көрүнүшү мүмкүн, анткени бул учурда конструкторго сап берилгенин көрүп жатабыз, ошондуктан аны int түрүнө которууга аракет кылышыбыз күмөн. Бирок, иштеп чыгуу учурунда биз id ичиндеги маани кайсы түрдү билдирерин билбей калышыбыз мүмкүн жана бул учурда санды алууга аракет кылып жатканда, java.lang.ClassCastException өзгөчө учуруна туш болобуз.
Ар бир жеке түр үчүн Каттоо классынын өзүнүн версиясын жазуу да жакшы чечим эмес, анткени бул учурда биз өзүбүздү кайталоого аргасыз болобуз.
Бул көйгөйлөр жалпылоолорду же генериктерди жок кылууга багытталган. Генериктер колдонула турган конкреттүү түрдү көрсөтпөөгө мүмкүндүк берет. Келгиле, Account классын жалпы катары аныктайлы:
class Account<T>{
private T id;
private int sum;
Account(T id, int sum){
this.id = id;
this.sum = sum;
}
public T getId() { return id; }
public int getSum() { return sum; }
public void setSum(int sum) { this.sum = sum; }
}
Account<T> классынын аныктамасында T тамгасын колдонуу менен, биз берилген T түрү бул класс тарабынан колдонулаарын көрсөтөбүз. Бурчтуу кашаадагы T параметри жалпы параметр деп аталат, анткени анын ордуна каалаган түрүн алмаштырууга болот. Ошол эле учурда, ал кандай тип болорун азырынча билбейбиз: String, int же башка. Мындан тышкары, T тамгасы шарттуу түрдө тандалат, ал башка тамга же белгилердин топтому болушу мүмкүн.
Классты жарыялагандан кийин, биз T жалпы параметрин колдоно алабыз: ушул сыяктуу типтеги өзгөрмө класста жарыяланып, андан кийин конструктордо маани ыйгарылат.
getId() ыкмасы id өзгөрмөнүн маанисин кайтарат, бирок бул өзгөрмө T түрүн билдиргендиктен, бул ыкма T түрүндөгү объектти да кайтарат: public T getId().
Биз бул классты колдонобуз:
public class Program{
public static void main(String[] args) {
Account<String> acc1 = new Account<String>("2345", 5000);
String acc1Id = acc1.getId();
System.out.println(acc1Id);
Account<Integer> acc2 = new Account<Integer>(2345, 5000);
Integer acc2Id = acc2.getId();
System.out.println(acc2Id);
}
}
class Account<T>{
private T id;
private int sum;
Account(T id, int sum){
this.id = id;
this.sum = sum;
}
public T getId() { return id; }
public int getSum() { return sum; }
public void setSum(int sum) { this.sum = sum; }
}
Бул класстын өзгөрмөсүн аныктоодо жана объектти түзүүдө бурчтуу кашаанын ичинде класстын аталышынан кийин жалпы параметрдин ордуна кайсы тип колдонулаарын көрсөтүү керек. Алар объектилер менен гана иштешет, бирок примитивдүү типтер менен иштебей турганын эске алуу керек. Башкача айтканда, биз Account<Integer> жаза алабыз, бирок биз int же double түрүн колдоно албайбыз, мисалы, Account<int>. Примитивдик типтердин ордуна орогуч класстары колдонулушу керек: int ордуна бүтүн, double ордуна Double ж.б.
Мисалы, биринчи объект String түрүн колдонот, башкача айтканда, String T ордуна алмаштырылат:
Account<String> acc1 = new Account<String>("2345", 5000);
Бул учурда конструкторго биринчи параметр катары сап берилет.
Ал эми экинчи объект int (Integer) түрүн колдонот:
Account<Integer> acc2 = new Account<Integer>(2345, 5000);
Жалпы интерфейстер
Интерфейстер класстар сыяктуу эле жалпы болушу мүмкүн. Келгиле, жалпы эсептик интерфейсти түзүп, аны программада колдонолу:
public class Program{
public static void main(String[] args) {
Accountable<String> acc1 = new Account("1235rwr", 5000);
Account acc2 = new Account("2373", 4300);
System.out.println(acc1.getId());
System.out.println(acc2.getId());
}
}
interface Accountable<T>{
T getId();
int getSum();
void setSum(int sum);
}
class Account implements Accountable<String>{
private String id;
private int sum;
Account(String id, int sum){
this.id = id;
this.sum = sum;
}
public String getId() { return id; }
public int getSum() { return sum; }
public void setSum(int sum) { this.sum = sum; }
}
Мындай интерфейсти ишке ашыруунун эки стратегиясы бар. Бул учурда, биринчи стратегия ишке ашырылат, качан жалпы интерфейс параметри белгилүү бир тип менен ишке ашырылган, мисалы, бул учурда Стринг түрү. Андан кийин интерфейсти ишке ашырган класс ошол түргө туташтырылган.
Экинчи стратегия жалпы класстын аныктамасын киргизет, ал дагы ошол эле жалпы параметрди колдонот:
public class Program{
public static void main(String[] args) {
Account<String> acc1 = new Account<String>("1235rwr", 5000);
Account<String> acc2 = new Account<String>("2373", 4300);
System.out.println(acc1.getId());
System.out.println(acc2.getId());
}
}
interface Accountable<T>{
T getId();
int getSum();
void setSum(int sum);
}
class Account<T> implements Accountable<T>{
private T id;
private int sum;
Account(T id, int sum){
this.id = id;
this.sum = sum;
}
public T getId() { return id; }
public int getSum() { return sum; }
public void setSum(int sum) { this.sum = sum; }
}
Жалпы методдор
Жалпы түрлөрүнөн тышкары, сиз жалпы параметрлерди ушундай эле жол менен колдоно турган жалпы ыкмаларды түзө аласыз. Мисалы:
public class Program{
public static void main(String[] args) {
Printer printer = new Printer();
String[] people = {"Tom", "Alice", "Sam", "Kate", "Bob", "Helen"};
Integer[] numbers = {23, 4, 5, 2, 13, 456, 4};
printer.<String>print(people);
printer.<Integer>print(numbers);
}
}
class Printer{
public <T> void print(T[] items){
for(T item: items){
System.out.println(item);
}
}
}
Жалпы ыкманын өзгөчөлүгү - бардык модификаторлордон кийин жана кайтаруу түрүнө чейин ыкма декларациясында жалпы параметрди колдонуу.
public <T> void print(T[] items)
Андан кийин, ыкманын ичинде, T түрүндөгү бардык маанилер берилген жалпы параметрди билдирет.
printer.<String>print(people);
printer.<Integer>print(numbers);
Бир нече жалпы параметрлерди колдонуу
Биз бир эле учурда бир нече универсалдуу параметрлерди орното алабыз:
public class Program{
public static void main(String[] args) {
Account<String, Double> acc1 = new Account<String, Double>("354", 5000.87);
String id = acc1.getId();
Double sum = acc1.getSum();
System.out.printf("Id: %s Sum: %f \n", id, sum);
}
}
class Account<T, S>{
private T id;
private S sum;
Account(T id, S sum){
this.id = id;
this.sum = sum;
}
public T getId() { return id; }
public S getSum() { return sum; }
public void setSum(S sum) { this.sum = sum; }
}
Бул учурда T параметринин ордуна String түрү, ал эми Double түрү S параметринин ордуна өткөрүлөт.
Жалпы конструкторлор
Конструкторлор методдор сыяктуу эле жалпы болушу мүмкүн. Бул учурда, жалпы параметрлер конструктордун алдында бурчтук кашааларда да көрсөтүлөт:
public class Program{
public static void main(String[] args) {
Account acc1 = new Account("cid2373", 5000);
Account acc2 = new Account(53757, 4000);
System.out.println(acc1.getId());
System.out.println(acc2.getId());
}
}
class Account{
private String id;
private int sum;
<T>Account(T id, int sum){
this.id = id.toString();
this.sum = sum;
}
public String getId() { return id; }
public int getSum() { return sum; }
public void setSum(int sum) { this.sum = sum; }
}
Бул учурда конструктор T түрүн билдирген id параметрин алат. Конструктордо анын мааниси сапка айландырылат жана локалдык өзгөрмөдө сакталат.