การใช้คลาสที่ไม่ระบุชื่อใน Java คืออะไร? เราสามารถพูดได้ว่าการใช้คลาสที่ไม่ระบุชื่อเป็นหนึ่งในข้อดีของ Java หรือไม่?
การใช้คลาสที่ไม่ระบุชื่อใน Java คืออะไร? เราสามารถพูดได้ว่าการใช้คลาสที่ไม่ระบุชื่อเป็นหนึ่งในข้อดีของ Java หรือไม่?
คำตอบ:
โดย "ชั้นไม่ระบุชื่อ" ฉันจะเอามันที่คุณหมายถึงระดับชั้นที่ไม่ระบุชื่อ
คลาสภายในที่ไม่ระบุชื่ออาจมีประโยชน์เมื่อสร้างอินสแตนซ์ของวัตถุที่มี "ความพิเศษ" บางอย่างเช่นวิธีการเอาชนะโดยไม่ต้องคลาสย่อยจริง ๆ
ฉันมักจะใช้เป็นทางลัดสำหรับการแนบ listener เหตุการณ์:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// do something
}
});
การใช้วิธีนี้ทำให้การเขียนโค้ดเร็วขึ้นเล็กน้อยเนื่องจากฉันไม่จำเป็นต้องสร้างคลาสเพิ่มเติมที่ใช้งาน ActionListener
- ฉันสามารถสร้างอินสแตนซ์คลาสภายในแบบไม่ระบุชื่อได้โดยไม่ต้องแยกชั้นเรียนจริง
ฉันใช้เทคนิคนี้สำหรับงานที่ "เร็วและสกปรก" เท่านั้นซึ่งทำให้ทั้งชั้นรู้สึกว่าไม่จำเป็น การมีคลาสภายในที่ไม่ระบุชื่อหลายคลาสที่ทำสิ่งเดียวกันควรได้รับการปรับโครงสร้างให้เป็นคลาสจริงไม่ว่าจะเป็นคลาสภายในหรือคลาสที่แยกต่างหาก
overloading methods
และไม่overriding methods
?
คลาสภายในที่ไม่ระบุชื่อนั้นปิดอย่างมีประสิทธิภาพดังนั้นจึงสามารถใช้เพื่อเลียนแบบแลมบ์ดานิพจน์หรือ "ผู้รับมอบสิทธิ์" ตัวอย่างเช่นใช้อินเทอร์เฟซนี้:
public interface F<A, B> {
B f(A a);
}
คุณสามารถใช้สิ่งนี้โดยไม่ระบุชื่อเพื่อสร้างฟังก์ชั่นชั้นหนึ่งใน Java สมมติว่าคุณมีวิธีการดังต่อไปนี้ที่ส่งกลับหมายเลขแรกที่มากกว่า i ในรายการที่กำหนดหรือ i หากไม่มีตัวเลขใหญ่กว่า:
public static int larger(final List<Integer> ns, final int i) {
for (Integer n : ns)
if (n > i)
return n;
return i;
}
จากนั้นคุณมีวิธีอื่นที่ส่งกลับหมายเลขแรกที่น้อยกว่า i ในรายการที่กำหนดหรือถ้าไม่มีจำนวนใดจะน้อยกว่า:
public static int smaller(final List<Integer> ns, final int i) {
for (Integer n : ns)
if (n < i)
return n;
return i;
}
วิธีการเหล่านี้เกือบจะเหมือนกัน ด้วยการใช้ฟังก์ชั่นเฟิร์สคลาสประเภท F เราสามารถเขียนสิ่งเหล่านี้เป็นวิธีการหนึ่งดังนี้:
public static <T> T firstMatch(final List<T> ts, final F<T, Boolean> f, T z) {
for (T t : ts)
if (f.f(t))
return t;
return z;
}
คุณสามารถใช้คลาสที่ไม่ระบุชื่อเพื่อใช้เมธอด firstMatch:
F<Integer, Boolean> greaterThanTen = new F<Integer, Boolean> {
Boolean f(final Integer n) {
return n > 10;
}
};
int moreThanMyFingersCanCount = firstMatch(xs, greaterThanTen, x);
นี่เป็นตัวอย่างที่คาดการณ์ไว้จริง ๆ แต่มันง่ายที่จะเห็นว่าการส่งผ่านฟังก์ชั่นราวกับว่ามันเป็นค่าเป็นคุณสมบัติที่มีประโยชน์มาก โปรดดูที่"ภาษาการเขียนโปรแกรมของคุณสามารถทำได้"โดย Joel เอง
ไลบรารีที่ดีสำหรับการเขียนโปรแกรม Java ในรูปแบบนี้: Functional Java
คลาสภายในแบบไม่ระบุชื่อถูกใช้ในสถานการณ์ต่อไปนี้:
1. ) สำหรับการเอาชนะ (การจัดหมวดหมู่ย่อย) เมื่อการกำหนดคลาสไม่สามารถใช้งานได้ยกเว้นกรณีปัจจุบัน:
class A{
public void methodA() {
System.out.println("methodA");
}
}
class B{
A a = new A() {
public void methodA() {
System.out.println("anonymous methodA");
}
};
}
2. ) สำหรับการนำอินเตอร์เฟสไปใช้เมื่อต้องการการนำอินเตอร์เฟสไปใช้งานสำหรับกรณีปัจจุบันเท่านั้น:
interface interfaceA{
public void methodA();
}
class B{
interfaceA a = new interfaceA() {
public void methodA() {
System.out.println("anonymous methodA implementer");
}
};
}
3. ) อาร์กิวเมนต์ของคลาสภายในที่ไม่ระบุชื่อ:
interface Foo {
void methodFoo();
}
class B{
void do(Foo f) { }
}
class A{
void methodA() {
B b = new B();
b.do(new Foo() {
public void methodFoo() {
System.out.println("methodFoo");
}
});
}
}
บางครั้งฉันใช้พวกเขาเป็นแฮ็กไวยากรณ์สำหรับการเริ่มต้นแผนที่
Map map = new HashMap() {{
put("key", "value");
}};
VS
Map map = new HashMap();
map.put("key", "value");
มันช่วยประหยัดความซ้ำซ้อนเมื่อทำงบจำนวนมาก อย่างไรก็ตามฉันยังพบปัญหาในการทำเช่นนี้เมื่อคลาสภายนอกต้องได้รับการทำให้เป็นอนุกรมผ่าน remoting
พวกเขามักใช้เป็นรูปแบบของการโทรกลับ verbose
ฉันคิดว่าคุณสามารถพูดได้ว่าพวกเขาได้เปรียบเมื่อเทียบกับการไม่มีพวกเขาและต้องสร้างชั้นเรียนที่มีชื่อทุกครั้ง แต่แนวคิดที่คล้ายกันถูกนำไปใช้ในภาษาอื่น ๆ ได้ดีกว่ามาก (เช่นการปิดหรือบล็อก)
นี่คือตัวอย่างการแกว่ง
myButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
// do stuff here...
}
});
แม้ว่ามันจะยังคงเป็นเรื่องที่ละเอียด แต่ก็ดีกว่าบังคับให้คุณกำหนดคลาสที่มีชื่อสำหรับผู้ฟังที่ถูกโยนทิ้งทุกคนเช่นนี้ (แม้ว่าขึ้นอยู่กับสถานการณ์และการใช้ซ้ำซึ่งอาจเป็นวิธีที่ดีกว่า)
myButton.addActionListener(e -> { /* do stuff here */})
หรือmyButton.addActionListener(stuff)
จะเป็น terer
คุณใช้มันในสถานการณ์ที่คุณจำเป็นต้องสร้างคลาสเพื่อวัตถุประสงค์เฉพาะภายในฟังก์ชั่นอื่นเช่นในฐานะผู้ฟังเป็น runnable (เพื่อวางไข่เธรด) ฯลฯ
ความคิดคือคุณเรียกพวกเขาจากภายในโค้ดของฟังก์ชั่นดังนั้นคุณไม่เคยอ้างถึงพวกเขาที่อื่นดังนั้นคุณไม่จำเป็นต้องตั้งชื่อพวกเขา คอมไพเลอร์เพียงระบุพวกเขา
พวกเขาเป็นน้ำตาล syntactic เป็นหลักและโดยทั่วไปควรจะย้ายไปที่อื่นเมื่อพวกเขาเติบโตที่ใหญ่กว่า
ฉันไม่แน่ใจว่ามันเป็นหนึ่งในข้อดีของ Java แต่ถ้าคุณใช้พวกเขา (และเราทุกคนใช้พวกเขาโชคไม่ดี) แล้วคุณสามารถยืนยันว่าพวกเขาเป็นหนึ่ง
GuideLines สำหรับ Class Anonymous
คลาสที่ไม่ระบุชื่อถูกประกาศและเริ่มต้นพร้อมกัน
คลาสที่ไม่ระบุชื่อต้องขยายหรือนำไปใช้กับหนึ่งหรือหนึ่งคลาสเท่านั้น
คลาส anonymouse ไม่มีชื่อจึงสามารถใช้ได้เพียงครั้งเดียว
เช่น:
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
});
ref.getClass().newInstance()
คุณสามารถได้รับกรณีหลายชั้นที่ไม่ระบุชื่อกับการสะท้อนเช่น
ใช่คลาสภายในที่ไม่ระบุชื่อเป็นหนึ่งในข้อดีของ Java อย่างแน่นอน
ด้วยคลาสภายในที่ไม่ระบุชื่อคุณจะสามารถเข้าถึงตัวแปรสุดท้ายและตัวแปรสมาชิกของคลาสที่ล้อมรอบได้และนั่นมีประโยชน์ในการฟัง ฯลฯ
แต่ข้อได้เปรียบที่สำคัญคือรหัสชั้นในซึ่งเป็น (อย่างน้อยควรจะเป็น) คู่อย่างแน่นหนากับคลาส / เมธอด / บล็อกโดยรอบมีบริบทเฉพาะ (คลาสโดยรอบเมธอดและบล็อก)
new Thread() {
public void run() {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
System.out.println("Exception message: " + e.getMessage());
System.out.println("Exception cause: " + e.getCause());
}
}
}.start();
นี่เป็นหนึ่งในตัวอย่างสำหรับประเภท Inner ที่ไม่ระบุชื่อโดยใช้เธรด
ฉันใช้วัตถุที่ไม่ระบุชื่อเพื่อโทรเข้าเธรดใหม่ ..
new Thread(new Runnable() {
public void run() {
// you code
}
}).start();
ระดับชั้นมีความเกี่ยวข้องกับอินสแตนซ์ของชั้นนอกและมีสองชนิดพิเศษระดับท้องถิ่นและระดับไม่ประสงค์ออกนาม คลาสที่ไม่ระบุชื่อช่วยให้เราสามารถประกาศและยกตัวอย่างคลาสได้ในเวลาเดียวกันจึงทำให้รหัสกระชับ เราใช้พวกเขาเมื่อเราต้องการชั้นเรียนเพียงครั้งเดียวเพราะพวกเขาไม่มีชื่อ
ลองพิจารณาตัวอย่างจากdocที่เรามีPerson
คลาส:
public class Person {
public enum Sex {
MALE, FEMALE
}
String name;
LocalDate birthday;
Sex gender;
String emailAddress;
public int getAge() {
// ...
}
public void printPerson() {
// ...
}
}
และเรามีวิธีพิมพ์สมาชิกที่ตรงกับเกณฑ์การค้นหาดังนี้:
public static void printPersons(
List<Person> roster, CheckPerson tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
CheckPerson
อินเตอร์เฟสอยู่ที่ไหน:
interface CheckPerson {
boolean test(Person p);
}
ตอนนี้เราสามารถใช้คลาสที่ไม่ระบุชื่อซึ่งใช้อินเทอร์เฟซนี้เพื่อระบุเกณฑ์การค้นหาเป็น:
printPersons(
roster,
new CheckPerson() {
public boolean test(Person p) {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
}
);
ที่นี่อินเทอร์เฟซง่ายมากและไวยากรณ์ของคลาสที่ไม่ระบุชื่อดูเหมือนเทอะทะและไม่ชัดเจน
Java 8ได้เปิดตัวฟังก์ชั่นการใช้งานคำซึ่งเป็นอินเตอร์เฟซที่มีเพียงนามธรรมวิธีหนึ่งดังนั้นเราสามารถพูดได้ว่าCheckPerson
เป็นอินเตอร์เฟซการทำงาน เราสามารถใช้แลมบ์ดานิพจน์ซึ่งช่วยให้เราสามารถส่งผ่านฟังก์ชันเป็นอาร์กิวเมนต์เมธอดเป็น:
printPersons(
roster,
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
เราสามารถใช้ส่วนต่อประสานการทำงานมาตรฐานPredicate
แทนส่วนต่อประสานCheckPerson
ซึ่งจะช่วยลดจำนวนรหัสที่ต้องการ
คลาสภายในที่ไม่ระบุชื่ออาจมีประโยชน์ในขณะที่ให้การใช้งานที่แตกต่างกันสำหรับวัตถุที่แตกต่างกัน แต่ควรใช้เท่าที่จำเป็นเพราะจะสร้างปัญหาในการอ่านโปรแกรม
หนึ่งในการใช้งานที่สำคัญของการเรียนที่ไม่ระบุชื่อในชั้นการสรุปซึ่งเรียกว่าfinalizer ผู้ปกครอง ในโลกของ Java ที่ใช้วิธีการสุดท้ายควรหลีกเลี่ยงจนกว่าคุณจะต้องการจริงๆ คุณต้องจำไว้ว่าเมื่อคุณแทนที่เมธอด finalize สำหรับคลาสย่อยคุณควรเรียกใช้เสมอsuper.finalize()
เช่นกันเพราะวิธีการสุดท้ายของซุปเปอร์คลาสจะไม่เรียกใช้โดยอัตโนมัติและคุณอาจมีปัญหากับหน่วยความจำรั่ว
ดังนั้นเมื่อพิจารณาข้อเท็จจริงที่กล่าวข้างต้นคุณสามารถใช้คลาสที่ไม่ระบุชื่อเช่น:
public class HeavyClass{
private final Object finalizerGuardian = new Object() {
@Override
protected void finalize() throws Throwable{
//Finalize outer HeavyClass object
}
};
}
การใช้เทคนิคนี้ช่วยให้คุณผ่อนคลายตัวคุณเองและนักพัฒนาซอฟต์แวร์คนอื่น ๆ เพื่อเรียกsuper.finalize()
ใช้คลาสย่อยHeavyClass
ที่ต้องการวิธีการสรุป
คุณสามารถใช้คลาสที่ไม่ระบุชื่อด้วยวิธีนี้
TreeSet treeSetObj = new TreeSet(new Comparator()
{
public int compare(String i1,String i2)
{
return i2.compareTo(i1);
}
});
ดูเหมือนไม่มีใครพูดถึงที่นี่ แต่คุณสามารถใช้คลาสที่ไม่ระบุชื่อเพื่อเก็บอาร์กิวเมนต์ประเภททั่วไป(ซึ่งปกติจะหายไปเนื่องจากการลบประเภท) :
public abstract class TypeHolder<T> {
private final Type type;
public TypeReference() {
// you may do do additional sanity checks here
final Type superClass = getClass().getGenericSuperclass();
this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
}
public final Type getType() {
return this.type;
}
}
หากคุณจะยกตัวอย่างคลาสนี้ด้วยวิธีที่ไม่ระบุชื่อ
TypeHolder<List<String>, Map<Ineger, Long>> holder =
new TypeHolder<List<String>, Map<Ineger, Long>>() {};
เช่นนั้น holder
อินสแตนซ์จะมีคำจำกัดความที่ไม่ถูกลบประเภทที่ผ่าน
สิ่งนี้มีประโยชน์มากสำหรับการสร้างเครื่องมือตรวจสอบ / deserializators นอกจากนี้คุณสามารถยกตัวอย่างประเภททั่วไปมีการสะท้อน(ดังนั้นหากคุณเคยต้องการที่จะทำnew T()
ในรูปแบบ parametrized - คุณจะยินดีที่)
วิธีที่ดีที่สุดในการปรับรหัสให้เหมาะสม นอกจากนี้เราสามารถใช้สำหรับวิธีการเอาชนะของคลาสหรืออินเตอร์เฟซ
import java.util.Scanner;
abstract class AnonymousInner {
abstract void sum();
}
class AnonymousInnerMain {
public static void main(String []k){
Scanner sn = new Scanner(System.in);
System.out.println("Enter two vlaues");
int a= Integer.parseInt(sn.nextLine());
int b= Integer.parseInt(sn.nextLine());
AnonymousInner ac = new AnonymousInner(){
void sum(){
int c= a+b;
System.out.println("Sum of two number is: "+c);
}
};
ac.sum();
}
}
ไม่ประสงค์ออกนามภายในชั้นจะใช้ในการสร้างวัตถุที่ไม่เคยจะถูกอ้างถึงอีกครั้ง มันไม่มีชื่อและถูกประกาศและสร้างในคำสั่งเดียวกัน สิ่งนี้ถูกใช้ในตำแหน่งที่ปกติคุณจะใช้ตัวแปรของวัตถุ คุณเปลี่ยนตัวแปรที่มีnew
คำหลักที่เรียกร้องให้ผู้สร้างและภายในกำหนดชั้นและ{
}
เมื่อเขียนเธรดโปรแกรมใน Java มักจะมีลักษณะเช่นนี้
ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();
ผู้ThreadClass
ใช้ที่นี่จะเป็นผู้กำหนด คลาสนี้จะใช้Runnable
อินเตอร์เฟสที่จำเป็นสำหรับการสร้างเธรด ในวิธี (เฉพาะวิธีการใน) จะต้องดำเนินการเช่นกัน เป็นที่ชัดเจนว่าการกำจัดThreadClass
run()
Runnable
ThreadClass
จะมีประสิทธิภาพมากขึ้นและนั่นคือเหตุผลว่าทำไมจึงมีคลาสภายในที่ไม่ระบุชื่อ
ดูรหัสต่อไปนี้
Thread runner = new Thread(new Runnable() {
public void run() {
//Thread does it's work here
}
});
runner.start();
รหัสนี้จะแทนที่การอ้างอิงที่ทำtask
ในตัวอย่างด้านบนสุด แทนที่จะมีคลาสแยกต่างหาก Anonymous Inner Class ภายในThread()
Constructor ส่งคืนออบเจกต์ที่ไม่มีชื่อที่ใช้Runnable
อินเทอร์เฟซและแทนที่run()
เมธอด วิธีการrun()
จะรวมคำสั่งที่อยู่ภายในซึ่งทำงานตามที่เธรดต้องการ
ตอบคำถามว่า Anonymous Inner Classes เป็นหนึ่งในข้อดีของ Java หรือไม่ฉันต้องบอกว่าฉันค่อนข้างแน่ใจเพราะฉันไม่คุ้นเคยกับภาษาการเขียนโปรแกรมหลายภาษาในขณะนี้ แต่สิ่งที่ฉันสามารถพูดได้ก็คือมันเป็นวิธีการเข้ารหัสที่รวดเร็วและง่ายขึ้น
ข้อมูลอ้างอิง: Sams สั่งสอนตัวเองด้วยจาวาในรุ่นที่ 21
ข้อดีอีกข้อหนึ่ง: ดัง
ที่คุณทราบว่า Java ไม่สนับสนุนการสืบทอดหลายอย่างดังนั้นหากคุณใช้คลาส kinda "Thread" เป็นคลาสที่ไม่ระบุชื่อคลาสยังมีพื้นที่ว่างเหลืออีกหนึ่งคลาสที่จะขยาย