ממשקים בג'אווה

ממשקי Java שונים משיעורים, וחשוב לדעת כיצד להשתמש במאפיינים המיוחדים שלהם בתוכניות Java שלך. מדריך זה מציג את ההבדל בין מחלקות וממשקים, ואז מדריך אותך בדוגמאות המדגימות כיצד להכריז, ליישם ולהרחיב ממשקי Java.

תוכלו ללמוד גם כיצד הממשק התפתח בג'אווה 8, בתוספת שיטות ברירת מחדל וסטטיות, ובג'אווה 9 בשיטות הפרטיות החדשות. תוספות אלה הופכות את הממשקים לשימושיים יותר עבור מפתחים מנוסים. למרבה הצער, הם גם מטשטשים את הקווים בין כיתות וממשקים, מה שהופך את תכנות הממשק למבלבל עוד יותר למתחילים בג'אווה.

הורד קבל את הקוד הורד את קוד המקור למשל יישומים במדריך זה. נוצר על ידי ג'ף פרייזן עבור JavaWorld.

מהו ממשק Java?

ממשק נקודה שבה שתי מערכות לפגוש וליצור אינטראקציה. לדוגמה, ייתכן שתשתמש בממשק מכונות אוטומטיות לבחירת פריט, לשלם עבורו ולקבל פריט מזון או שתייה. מנקודת מבט תכנותית, ממשק יושב בין רכיבי תוכנה. קחו בחשבון שממשק כותרת שיטה (שם השיטה, רשימת הפרמטרים וכן הלאה) יושב בין קוד חיצוני שקורא לשיטה לקוד בתוך השיטה שתבוצע כתוצאה מהשיחה. הנה דוגמה:

System.out.println(average(10, 15)); double average(double x, double y) // interface between average(10, 15) call and return (x + y) / 2; { return (x + y) / 2; }

מה שמבלבל לעתים קרובות למתחילים בג'אווה הוא שיש בשיעורים גם ממשקים. כפי שהסברתי בג'אווה 101: מחלקות ואובייקטים בג'אווה, הממשק הוא החלק של הכיתה הנגיש לקוד שנמצא מחוץ לו. הממשק של הכיתה מורכב משילוב כלשהו של שיטות, שדות, בונים ויישויות אחרות. שקול רישום 1.

רישום 1. מחלקת החשבון והממשק שלה

class Account { private String name; private long amount; Account(String name, long amount) { this.name = name; setAmount(amount); } void deposit(long amount) { this.amount += amount; } String getName() { return name; } long getAmount() { return amount; } void setAmount(long amount) { this.amount = amount; } }

Account(String name, long amount)בנאי ואת void deposit(long amount), String getName(), long getAmount(), ו void setAmount(long amount)שיטות ליצור את Accountהממשק של הכיתה: הם נגישים קוד חיצוני. private String name;ואת private long amount;השדות אינם נגישים.

מידע נוסף על ממשקי Java

מה אתה יכול לעשות עם ממשקים בתוכניות Java שלך? קבל סקירה עם ששת התפקידים של ג'ף של ממשק Java.

הקוד של שיטה, אשר תומך בממשק של השיטה, וכי חלק הכיתה התומך בממשק של הכיתה (כגון שדות פרטיים) ידוע בתור של או כיתת השיטה ביצוע . יש להסתיר יישום מקוד חיצוני כדי שניתן יהיה לשנות אותו כדי לעמוד בדרישות המתפתחות.

כאשר יישומים נחשפים, יכולים להיווצר תלות הדדית בין רכיבי תוכנה. לדוגמה, קוד השיטה עשוי להסתמך על משתנים חיצוניים ומשתמשי הכיתה עשויים להיות תלויים בשדות שהיו צריכים להיות מוסתרים. צימוד זה יכול לגרום לבעיות כאשר יישומים חייבים להתפתח (אולי יש להסיר שדות חשופים).

מפתחי ג'אווה משתמשים בתכונת שפת הממשק כדי להפשט ממשקי כיתות, וכך לנתק את הכיתות מהמשתמשים שלהם. על ידי התמקדות בממשקי Java במקום בכיתות, תוכל למזער את מספר ההפניות לשמות המחלקות בקוד המקור שלך. זה מאפשר מעבר ממעמד אחד למשנהו (אולי כדי לשפר ביצועים) עם התבגרות התוכנה שלך. הנה דוגמה:

List names = new ArrayList() void print(List names) { // ... }

דוגמה זו מצהירה ומאתחלת namesשדה המאחסן רשימה של שמות מחרוזות. הדוגמה מצהירה גם על print()שיטה להדפסת התוכן של רשימת מחרוזות, אולי מחרוזת אחת בכל שורה. ליתר קיצור, השמטתי את יישום השיטה.

Listהוא ממשק Java המתאר אוסף רציף של אובייקטים. ArrayListהוא מחלקה המתארת ​​יישום מבוסס מערך של Listממשק Java. מופע חדש של ArrayListהמחלקה מתקבל ומוקצה Listלמשתנה names. ( Listו ArrayListמאוחסן של מעמד ספריית תקן java.utilחבילה.)

סוגרי זווית וגנריות

סוגרי הזווית ( <ו- >) הם חלק ממערך התכונות הגנריות של Java. הם מציינים namesשמתארים רשימת מחרוזות (ניתן לשמור רק מחרוזות ברשימה). אציג גנריות במאמר עתידי ב- Java 101.

כאשר קוד לקוח מתקשר איתו names, הוא יפעיל את השיטות המוצהרות על ידי Listואשר מיושמות על ידי ArrayList. קוד הלקוח לא יתקשר ישירות עם ArrayList. כתוצאה מכך, קוד הלקוח לא יישבר כאשר נדרש מחלקת יישום אחרת, כגון LinkedList:

List names = new LinkedList() // ... void print(List names) { // ... }

מכיוון print()שסוג הפרמטר של השיטה הוא List, יישום שיטה זו אינו חייב להשתנות. עם זאת, אם הסוג היה ArrayList, היה צריך לשנות את הסוג ל LinkedList. אם שני השיעורים יצהירו על שיטות ייחודיות משלהם, יתכן שתצטרך לשנות print()את היישום באופן משמעותי .

צימוד Listבין ArrayListלבין LinkedListמאפשר לך לכתוב קוד זה חיסון לשינויים-יישום כיתה. באמצעות ממשקי Java תוכלו להימנע מבעיות שעלולות לנבוע מהסתמכות על שיעורי יישום. ניתוק זה הוא הסיבה העיקרית לשימוש בממשקי Java.

הכרזת ממשקי Java

אתה מצהיר על ממשק על ידי הקפדה על תחביר דמוי כיתה המורכב מכותרת ואחריה גוף. לכל הפחות הכותרת מורכבת ממילת מפתח interfaceואחריה שם שמזהה את הממשק. הגוף מתחיל באופי סד פתוח ומסתיים בסד צמוד. בין התיחומים הללו יש הצהרות כותרות קבועות וכותרות שיטה:

interface identifier { // interface body }

לפי האמנה, האות הראשונה בשם הממשק באותיות רישיות ואותיות שלאחר מכן באותיות קטנות (למשל Drawable). אם שם מורכב ממספר מילים, האות הראשונה של כל מילה באותיות רישיות (כגון DrawableAndFillable). כינוס שמות זה מכונה CamelCasing.

רישום 2 מכריז על ממשק בשם Drawable.

רישום 2. דוגמה לממשק Java

interface Drawable { int RED = 1; int GREEN = 2; int BLUE = 3; int BLACK = 4; int WHITE = 5; void draw(int color); }

ממשקים בספריית המחלקות הסטנדרטית של Java

כמוסכמת שמות, ממשקים רבים בספריית המחלקות הסטנדרטית של Java מסתיימים בסיומת מסוגלת . דוגמאות לכך כוללות Callable, Cloneable, Comparable, Formattable, Iterable, Runnable, Serializable, ו Transferable. הסיומת אינה חובה, עם זאת; הספרייה בכיתה הרגילה כוללת את הממשקים CharSequence, ClipboardOwner, Collection, Executor, Future, Iterator, List, Mapועוד רבים אחרים.

Drawableמצהיר על חמישה שדות המזהים קבועי צבע. ממשק זה מכריז גם על הכותרת draw()לשיטה שיש להתקשר אליה עם אחד הקבועים הללו כדי לציין את הצבע המשמש לשרטוט מתאר. (שימוש בקבועים שלמים אינו רעיון טוב משום שניתן להעביר אליו כל ערך שלם draw(). עם זאת, הם מספיקים בדוגמה פשוטה.)

ברירות מחדל של כותרת השדה והשיטה

שדות המוצהרים בממשק הם באופן מרומז public final static. כותרות השיטה של ​​ממשק הן באופן מרומז public abstract.

Drawableמזהה סוג הפניה שמציין מה לעשות (לצייר משהו) אך לא כיצד לעשות זאת. פרטי היישום מועברים לשיעורים המיישמים ממשק זה. מקרים של שיעורים כאלה מכונים ציור ציור מכיוון שהם יודעים לצייר את עצמם.

ממשקי סמן ותיוג

An interface with an empty body is known as a marker interface or a tagging interface. The interface exists only to associate metadata with a class. For example, Cloneable (see Inheritance in Java, Part 2) implies that instances of its implementing class can be shallowly cloned. When Object's clone() method detects (via runtime type identification) that the calling instance's class implements Cloneable, it shallowly clones the object.

Implementing Java interfaces

A class implements an interface by appending Java's implements keyword followed by a comma-separated list of interface names to the class header, and by coding each interface method in the class. Listing 3 presents a class that implements Listing 2's Drawable interface.

Listing 3. Circle implementing the Drawable interface

class Circle implements Drawable { private double x, y, radius; Circle(double x, double y, double radius) { this.x = x; this.y = y; this.radius = radius; } @Override public void draw(int color) { System.out.println("Circle drawn at (" + x + ", " + y + "), with radius " + radius + ", and color " + color); } double getRadius() { return radius; } double getX() { return x; } double getY() { return y; } }

Listing 3's Circle class describes a circle as a center point and a radius. As well as providing a constructor and suitable getter methods, Circle implements the Drawable interface by appending implements Drawable to the Circle header, and by overriding (as indicated by the @Override annotation) Drawable's draw() method header.

Listing 4 presents a second example: a Rectangle class that also implements Drawable.

Listing 4. Implementing the Drawable interface in a Rectangle context

class Rectangle implements Drawable { private double x1, y1, x2, y2; Rectangle(double x1, double y1, double x2, double y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } @Override public void draw(int color) { System.out.println("Rectangle drawn with upper-left corner at (" + x1 + ", " + y1 + ") and lower-right corner at (" + x2 + ", " + y2 + "), and color " + color); } double getX1() { return x1; } double getX2() { return x2; } double getY1() { return y1; } double getY2() { return y2; } }

Listing 4's Rectangle class describes a rectangle as a pair of points denoting the upper-left and lower-right corners of this shape. As with Circle, Rectangle provides a constructor and suitable getter methods, and also implements the Drawable interface.

Overriding interface method headers

The compiler reports an error when you attempt to compile a non-abstract class that includes an implements interface clause but doesn't override all of the interface's method headers.

An interface type's data values are the objects whose classes implement the interface and whose behaviors are those specified by the interface's method headers. This fact implies that you can assign an object's reference to a variable of the interface type, provided that the object's class implements the interface. Listing 5 demonstrates.

Listing 5. Aliasing Circle and Rectangle objects as Drawables

class Draw { public static void main(String[] args) { Drawable[] drawables = new Drawable[] { new Circle(10, 20, 15), new Circle(30, 20, 10), new Rectangle(5, 8, 8, 9) }; for (int i = 0; i < drawables.length; i++) drawables[i].draw(Drawable.RED); } }

Because Circle and Rectangle implement Drawable, Circle and Rectangle objects have Drawable type in addition to their class types. Therefore, it's legal to store each object's reference in an array of Drawables. A loop iterates over this array, invoking each Drawable object's draw() method to draw a circle or a rectangle.

Assuming that Listing 2 is stored in a Drawable.java source file, which is in the same directory as the Circle.java, Rectangle.java, and Draw.java source files (which respectively store Listing 3, Listing 4, and Listing 5), compile these source files via either of the following command lines:

javac Draw.java javac *.java

Run the Draw application as follows:

java Draw

You should observe the following output:

Circle drawn at (10.0, 20.0), with radius 15.0, and color 1 Circle drawn at (30.0, 20.0), with radius 10.0, and color 1 Rectangle drawn with upper-left corner at (5.0, 8.0) and lower-right corner at (8.0, 9.0), and color 1

Note that you could also generate the same output by specifying the following main() method:

public static void main(String[] args) { Circle c = new Circle(10, 20, 15); c.draw(Drawable.RED); c = new Circle(30, 20, 10); c.draw(Drawable.RED); Rectangle r = new Rectangle(5, 8, 8, 9); r.draw(Drawable.RED); }

כפי שאתה יכול לראות, זה מייגע להפעיל שוב ושוב את draw()השיטה של כל אובייקט . יתר על כן, פעולה זו מוסיפה קוד byt נוסף Drawלקובץ הכיתה. על ידי חשיבה של Circleו- Rectangleas Drawable, אתה יכול למנף מערך ולולאה פשוטה כדי לפשט את הקוד. זהו יתרון נוסף מעיצוב קוד להעדפת ממשקים על פני כיתות.

זְהִירוּת!