Интерфейстер
Мурас механизми абдан ыңгайлуу, бирок анын чектөөлөрү бар. Атап айтканда, биз бир класстан гана мурастай алабыз, мисалы, C++ тилинен айырмаланып, анда бир нече тукум куучулук бар.
Java тилинде интерфейстер бул маселени жарым-жартылай чечет. Интерфейстер конкреттүү ишке ашырууга ээ болбогон кээ бир функцияларды аныктайт, андан кийин бул интерфейстерди колдонгон класстар ишке ашырат. Жана бир класс көптөгөн интерфейстерди колдоно алат.
Интерфейсти аныктоо үчүн интерфейстин ачкыч сөзү колдонулат. Мисалы:
interface Printable{
void print();
}
Бул интерфейс Printable деп аталат. Интерфейс ишке ашырууга ээ же болбошу мүмкүн болгон константаларды жана методдорду аныкташы мүмкүн. Ишке ашыруусу жок методдор абстракттуу класстардын абстрактуу ыкмаларына окшош. Ошентип, бул учурда ишке ашырууга ээ болбогон бир ыкма жарыяланды.
Бардык интерфейс методдорунда кирүү модификаторлору жок, бирок чындыгында кирүү демейки боюнча жалпыга ачык, анткени интерфейстин максаты ишке ашыруу үчүн класстын функционалдуулугун аныктоо болуп саналат. Ошондуктан, бардык функциялар ишке ашыруу үчүн ачык болушу керек.
Класс интерфейсти колдонуу үчүн, сиз implements ачкыч сөзүн колдонушуңуз керек:
public class Program{
public static void main(String[] args) {
Book b1 = new Book("Java. Complete Referense.", "H. Shildt");
b1.print();
}
}
interface Printable{
void print();
}
class Book implements Printable{
String name;
String author;
Book(String name, String author){
this.name = name;
this.author = author;
}
public void print() {
System.out.printf("%s (%s) \n", name, author);
}
}
Бул учурда, Book классы Printable интерфейсин ишке ашырат. Ошол эле учурда, эгерде класс интерфейсти колдонсо, анда ал интерфейстин бардык ыкмаларын ишке ашыруусу керек экендигин эске алуу керек, жогоруда айтылгандай, басып чыгаруу ыкмасы ишке ашырылат. Андан кийин, негизги методдо, биз Book классынын объектисин түзүп, анын басып чыгаруу ыкмасын чакыра алабыз. Эгерде класс эч кандай интерфейстик методдорду ишке ашырбаса, анда мындай класс абстракттуу деп аныкталышы керек, ал эми анын абстракттуу эмес тукум класстары бул ыкмаларды ишке ашырышы керек.
Ошол эле учурда биз түздөн-түз интерфейс объекттерин түзө албайбыз, андыктан төмөнкү код иштебейт:
Printable pr = new Printable();
pr.print();
Интерфейстерди колдонуунун артыкчылыктарынын бири - алар колдонмоңузга ийкемдүүлүктү кошууга мүмкүндүк берет. Мисалы, Book классынан тышкары, Printable интерфейсин ишке ашыра турган дагы бир классты аныктайлы:
class Journal implements Printable {
private String name;
String getName(){
return name;
}
Journal(String name){
this.name = name;
}
public void print() {
System.out.println(name);
}
}
Book классы жана Journal классы басып чыгаруу интерфейсин ишке ашыруу менен байланышкан. Ошондуктан, биз эки класстын үлгүлөрү катары программада Басылып чыгуучу объекттерди динамикалык түрдө түзө алабыз:
public class Program{
public static void main(String[] args) {
Printable printable = new Book("Java. Complete Reference", "H. Shildt");
printable.print(); // Java. Complete Reference (H. Shildt)
printable = new Journal("Foreign Policy");
printable.print(); // Foreign Policy
}
}
interface Printable{
void print();
}
class Book implements Printable{
String name;
String author;
Book(String name, String author){
this.name = name;
this.author = author;
}
public void print() {
System.out.printf("%s (%s) \n", name, author);
}
}
class Journal implements Printable {
private String name;
String getName(){
return name;
}
Journal(String name){
this.name = name;
}
public void print() {
System.out.println(name);
}
}
Типти которуудагы интерфейстер
Типти өзгөртүү жөнүндө айтылгандардын баары интерфейстерге да тиешелүү. Мисалы, Journal классы Printable интерфейсин ишке ашыргандыктан, Printable өзгөрмө Journal объектисине шилтемени сактай алат:
Printable p =new Journal("Foreign Affairs");
p.print();
// Интерфейс не имеет метода getName, необходимо явное приведение
String name = ((Journal)p).getName();
System.out.println(name);
Жана эгер биз Journal классынын Printable интерфейсинде эмес, бирок Journal классынын өзүндө аныкталбаган методдоруна кирүүнү кааласак, анда типти конверсиялоону ачык аткарышыбыз керек: ((Journal)p).getName();
Демейки ыкмалар
JDK 8ге чейин интерфейсти ишке ашырууда анын бардык ыкмаларын класста ишке ашырууга туура келди. Ал эми интерфейстин өзү белгилүү бир ишке ашыруусуз гана метод аныктамаларын камтышы мүмкүн. JDK 8 демейки ыкмалар сыяктуу функцияларды кошкон. Эми интерфейстер методдорду аныктоодон тышкары, демейки ишке ашырылышы мүмкүн, эгерде бул интерфейсти ишке ашырган класс методду ишке ашырбаса колдонулат. Мисалы, басып чыгаруу интерфейсинде демейки ыкманы түзөлү:
interface Printable {
default void print(){
System.out.println("Undefined printable");
}
}
Демейки ыкма эч кандай өзгөрткүчсүз кадимки ыкма жана демейки ачкыч сөз менен белгиленет. Андан кийин, Journal классында бул ыкманы ишке ашыруунун кереги жок, бирок биз аны жокко чыгара алабыз:
class Journal implements Printable {
private String name;
String getName(){
return name;
}
Journal(String name){
this.name = name;
}
}
Статикалык методдор
JDK 8 статикалык методдору интерфейстерде бар болгондуктан, алар класстык методдорго окшош:
interface Printable {
void print();
static void read(){
System.out.println("Read printable");
}
}
Интерфейстин статикалык ыкмасына кайрылуу үчүн, класстардагыдай эле, интерфейстин атын жана методду жазыңыз:
public static void main(String[] args) {
Printable.read();
}
Жеке методдор
Демейки боюнча, интерфейстеги бардык методдор чындыгында коомдук өзгөрткүчкө ээ. Бирок, Java 9 болгондуктан, биз интерфейсте жеке ыкмаларды да аныктай алабыз. Алар статикалык же статикалык эмес болушу мүмкүн, бирок алардын демейки ишке ашырылышы мүмкүн эмес.
Мындай ыкмалар алар аныкталган интерфейстин ичинде гана колдонулушу мүмкүн. Башкача айтканда, мисалы, интерфейсте кээ бир кайталануучу аракеттерди аткарышыбыз керек жана бул учурда мындай аракеттерди жеке ыкмаларга бөлүүгө болот:
public class Program{
public static void main(String[] args) {
Calculatable c = new Calculation();
System.out.println(c.sum(1, 2));
System.out.println(c.sum(1, 2, 4));
}
}
class Calculation implements Calculatable{
}
interface Calculatable{
default int sum(int a, int b){
return sumAll(a, b);
}
default int sum(int a, int b, int c){
return sumAll(a, b, c);
}
private int sumAll(int... values){
int result = 0;
for(int n : values){
result += n;
}
return result;
}
}
Интерфейстеги константалар
Методдордон тышкары статикалык константаларды интерфейстерде аныктоого болот:
interface Stateable{
int OPEN = 1;
int CLOSED = 0;
void printState(int n);
}
Мындай константалардын да эч кандай модификаторлору жок болсо да, демейки боюнча аларда жалпы статикалык акыркы мүмкүндүк модификатору бар, ошондуктан алардын мааниси программанын каалаган жеринен жеткиликтүү.
Константаларды колдонуу:
public class Program{
public static void main(String[] args) {
WaterPipe pipe = new WaterPipe();
pipe.printState(1);
}
}
class WaterPipe implements Stateable{
public void printState(int n){
if(n==OPEN)
System.out.println("Water is opened");
else if(n==CLOSED)
System.out.println("Water is closed");
else
System.out.println("State is invalid");
}
}
interface Stateable{
int OPEN = 1;
int CLOSED = 0;
void printState(int n);
}
Интерфейстердин көп жолу ишке ашырылышы
Эгерде класста бир нече интерфейсти колдонуу керек болсо, анда алардын бардыгы аткарылат деген сөздөн кийин үтүр менен келтирилет:
interface Printable {
// методы интерфейса
}
interface Searchable {
// методы интерфейса
}
class Book implements Printable, Searchable{
// реализация класса
}
Интерфейстин мурастоо
Класстар сыяктуу интерфейстер тукум кууса болот:
interface BookPrintable extends Printable{
void paint();
}
Бул интерфейсти колдонууда Book классы BookPrintable интерфейсинин эки ыкмасын жана негизги Printable интерфейсинин методдорун ишке ашыруусу керек болот.
Ички(вложенные) интерфейстер
Класстар сыяктуу эле, интерфейстер уя салынышы мүмкүн, башкача айтканда, алар класстардын же башка интерфейстердин ичинде аныкталышы мүмкүн. Мисалы:
class Printer{
interface Printable {
void print();
}
}
Мындай интерфейсти колдонууда класстын аталышы менен бирге анын толук квалификациялуу атын көрсөтүүбүз керек:
public class Journal implements Printer.Printable {
String name;
Journal(String name){
this.name = name;
}
public void print() {
System.out.println(name);
}
}
Интерфейсти колдонуу мурунку учурларга окшош болот:
Printer.Printable p =new Journal("Foreign Affairs");
p.print();
Методдордун параметрлери жана натыйжалары катары интерфейстер
Жана класстар сыяктуу эле, интерфейстер методдун параметрлеринин түрү же кайтаруу түрү катары колдонулушу мүмкүн:
public class Program{
public static void main(String[] args) {
Printable printable = createPrintable("Foreign Affairs",false);
printable.print();
read(new Book("Java for impatients", "Cay Horstmann"));
read(new Journal("Java Dayly News"));
}
static void read(Printable p){
p.print();
}
static Printable createPrintable(String name, boolean option){
if(option)
return new Book(name, "Undefined");
else
return new Journal(name);
}
}
interface Printable{
void print();
}
class Book implements Printable{
String name;
String author;
Book(String name, String author){
this.name = name;
this.author = author;
}
public void print() {
System.out.printf("%s (%s) \n", name, author);
}
}
class Journal implements Printable {
private String name;
String getName(){
return name;
}
Journal(String name){
this.name = name;
}
public void print() {
System.out.println(name);
}
}
read() методу параметр катары Printable интерфейс объектисин алат, ошондуктан биз бул методго Book объектисин да, Journal объектисин да өткөрө алабыз.
CreatePrintable() методу Printable объектин кайтарат, ошондуктан биз Book объектин да, Journal объектисин да кайтара алабыз.
Консолго чыгаруу:
Foreign Affairs
Java for impatients (Cay Horstmann)
Java Dayly News