Bisakah kita instantiate kelas abstrak?

574

Dalam salah satu wawancara saya, saya ditanya "Jika kita bisa membuat kelas abstrak?"

Jawaban saya adalah "Tidak, kami tidak bisa". Tetapi, pewawancara mengatakan kepada saya, "Kita bisa salah."

Saya sedikit berdebat tentang ini. Kemudian dia mengatakan kepada saya untuk mencobanya sendiri di rumah.

abstract class my {
    public void mymethod() {
        System.out.print("Abstract");
    }
}

class poly {
    public static void main(String a[]) {
        my m = new my() {};
        m.mymethod();
    }
}

Di sini, saya membuat instance dari kelas saya dan memanggil metode kelas abstrak. Adakah yang bisa menjelaskan ini padaku? Apakah saya benar-benar salah selama wawancara?

Ravi
sumber
2
Meskipun hanya sedikit terkait, salah satu mungkin bisa instantiate kelas abstrak di C ++: jika Anda mendapatkan kelas non-abstrak Bdari satu abstrak A, selama bagian konstruksi dari Bcontoh, yang terdiri menjalankan A's konstruktor, objek jenis runtime sebenarnya A. Namun hanya sementara.
Vlad
8
@ jWeavers: Contoh yang dia berikan benar-benar salah. Anda seharusnya bertanya "lalu apa gunanya kelas abstrak" darinya. Jika Anda memperluasnya, lalu mengapa Anda membuat turunan dari kelas yang diperluas? Ini adalah objek yang sama sekali baru, di mana Anda berakhir tanpa data ..
Jus Lemon
3
Atau mungkin pewawancara ingin memeriksa seberapa yakin Anda tentang pernyataan Anda terhadap apa yang ia usulkan!
Sid
5
Dia berbohong padamu. Anda menjatuhkan bola ketika Anda gagal menunjukkan bahwa bukan itu kode ini dan menjelaskan apa subclass anonim itu. Dia mungkin sudah tahu itu dan ingin melihat apakah Anda tahu.
candied_orange
2
Ini bukan acara kuis, tapi wawancara kerja, kan? Jadi bagaimana jika Java, atau C ++, diizinkan membuat instance kelas abstrak? Anda tidak akan melakukannya, karena itu bukan hal yang cerdas untuk dilakukan. Di Objective-C, kelas abstrak hanya abstrak oleh konvensi, dan instantiating mereka adalah bug.
gnasher729

Jawaban:

723

Di sini, saya membuat instance dari kelas saya

Tidak, Anda tidak membuat instance dari kelas abstrak Anda di sini. Alih-alih, Anda membuat instance dari subclass anonim dari kelas abstrak Anda. Dan kemudian Anda memohon metode pada Anda referensi kelas abstrak menunjuk ke objek subclass .

Perilaku ini jelas tercantum dalam JLS - Bagian # 15.9.1 : -

Jika ekspresi penciptaan instance kelas berakhir dengan tubuh kelas, maka kelas yang dipakai adalah kelas anonim. Kemudian:

  • Jika T menunjukkan kelas, maka subclass langsung anonim dari kelas yang dinamai oleh T dinyatakan. Ini adalah kesalahan waktu kompilasi jika kelas yang dilambangkan dengan T adalah kelas akhir.
  • Jika T menunjukkan antarmuka, maka subclass langsung anonim Obyek yang mengimplementasikan antarmuka bernama T dinyatakan.
  • Dalam kedua kasus, tubuh subclass adalah ClassBody yang diberikan dalam ekspresi pembuatan instance kelas.
  • Kelas yang dipakai adalah subclass anonim.

Tekankan milikku.

Juga, di JLS - Bagian # 12.5 , Anda dapat membaca tentang Proses Pembuatan Objek . Saya akan mengutip satu pernyataan dari itu di sini: -

Setiap kali instance kelas baru dibuat, ruang memori dialokasikan untuknya dengan ruang untuk semua variabel instance yang dideklarasikan dalam tipe kelas dan semua variabel instance dideklarasikan dalam setiap superclass dari tipe kelas, termasuk semua variabel instance yang mungkin disembunyikan.

Tepat sebelum referensi ke objek yang baru dibuat dikembalikan sebagai hasilnya, konstruktor yang ditunjukkan diproses untuk menginisialisasi objek baru menggunakan prosedur berikut:

Anda dapat membaca tentang prosedur lengkap pada tautan yang saya berikan.


Untuk secara praktis melihat bahwa kelas yang dipakai adalah SubClass Anonim , Anda hanya perlu mengkompilasi kedua kelas Anda. Misalkan Anda meletakkan kelas-kelas itu dalam dua file yang berbeda:

My.java:

abstract class My {
    public void myMethod() {
        System.out.print("Abstract");
    }
}

Poly.java:

class Poly extends My {
    public static void main(String a[]) {
        My m = new My() {};
        m.myMethod();
    }
}

Sekarang, kompilasi kedua file sumber Anda:

javac My.java Poly.java

Sekarang di direktori tempat Anda menyusun kode sumber, Anda akan melihat file kelas berikut:

My.class
Poly$1.class  // Class file corresponding to anonymous subclass
Poly.class

Lihat kelas itu - Poly$1.class. Ini adalah file kelas yang dibuat oleh kompiler yang sesuai dengan subkelas anonim yang Anda instantiated menggunakan kode di bawah ini:

new My() {};

Jadi, jelas bahwa ada kelas yang berbeda sedang dipakai. Hanya saja, kelas itu diberi nama hanya setelah kompilasi oleh kompiler.

Secara umum, semua subclass anonim di kelas Anda akan dinamai dengan cara ini:

Poly$1.class, Poly$2.class, Poly$3.class, ... so on

Angka-angka itu menunjukkan urutan di mana kelas-kelas anonim muncul di kelas terlampir.

Rohit Jain
sumber
173
@coders. Jawaban yang tepat adalah: - Anda tidak dapat membuat instance kelas abstrak Anda, namun Anda dapat membuat instance subkelas konkret dari kelas abstrak Anda.
Rohit Jain
17
Dalam satu baris Anda dapat mengatakan: - Anda tidak pernah dapat membuat instance kelas abstrak. Itulah tujuan dari kelas abstrak.
Rahul Tripathi
66
Kedengarannya seperti pewawancara lebih banyak berinvestasi dalam jawabannya daripada dia dalam jawaban Anda ...
Neil T.
7
Menurut komentar lain (dengan referensi JLS ), "Suatu objek dikatakan sebagai turunan dari kelasnya dan dari semua superclasses dari kelasnya" - karena itu, bukankah kita sebenarnya secara teknis membuat turunan dari kelas abstrak di sini? yaitu instantiating kelas abstrak?
arshajii
6
@ RAD Saya akan mengatakan bahwa ada perbedaan antara menjadi instance ofdan instantiating. Anda hanya instantiate satu kelas, sedangkan objek yang Anda buat bisa menjadi instance dari beberapa kelas karena pewarisan.
Simon Forsberg
90

Di atas instantiates kelas batin anonim yang merupakan subkelas dari mykelas abstrak. Ini tidak sepenuhnya setara dengan instantiate kelas abstrak itu sendiri. OTOH, setiap turunan subclass adalah turunan dari semua kelas dan antarmuka supernya, sehingga sebagian besar kelas abstrak memang instantiated dengan membuat instance salah satu dari subkelas konkret mereka.

Jika pewawancara hanya mengatakan "salah!" tanpa menjelaskan, dan memberikan contoh ini, sebagai contoh tandingan yang unik, saya pikir dia tidak tahu apa yang dia bicarakan.

JB Nizet
sumber
10
Sebenarnya , superclass abstrak tidak dipakai. Konstruktornya dipanggil untuk menginisialisasi variabel instan.
Persepsi
4
Ya itu adalah: subclassInstance instanceof SuperClassakan mengembalikan true, jadi objeknya adalah instance dari superclass, yang berarti superclass telah di-instanciated. Tapi itu hanya tipuan semantik.
JB Nizet
5
Bisa jadi semantik. Java mendefinisikan instantiasi dalam hal membuat objek melalui kata kunci baru (yang tidak dapat Anda lakukan dengan kelas abstrak). Tetapi tentu saja subkelas konkret akan melaporkan dengan benar bahwa ini adalah turunan dari setiap anggota hierarki induknya.
Persepsi
11
paragraf 4.12.6 dari JLS mengatakan: "Suatu objek dikatakan sebagai turunan dari kelasnya dan dari semua superclasses dari kelasnya."
JB Nizet
85

= my() {};berarti bahwa ada implementasi anonim, tidak Instansiasi sederhana dari suatu obyek, yang seharusnya: = my(). Anda tidak pernah dapat membuat instance kelas abstrak.

Ioan
sumber
30

Hanya pengamatan yang bisa Anda lakukan:

  1. Mengapa polymeluas my? Ini tidak berguna ...
  2. Apa hasil kompilasi? Tiga file: my.class, poly.classdanpoly$1.class
  3. Jika kita dapat membuat instance kelas abstrak seperti itu, kita dapat membuat instance antarmuka terlalu ... aneh ...


Bisakah kita instantiate kelas abstrak?

Tidak, kami tidak bisa. Yang bisa kita lakukan adalah, membuat kelas anonim (itu file ketiga) dan instantiate.


Bagaimana dengan instantiasi kelas super?

Kelas super abstrak tidak dipakai oleh kami tetapi oleh java.

EDIT: Minta dia untuk menguji ini

public static final void main(final String[] args) {
    final my m1 = new my() {
    };
    final my m2 = new my() {
    };
    System.out.println(m1 == m2);

    System.out.println(m1.getClass().toString());
    System.out.println(m2.getClass().toString());

}

output adalah:

false
class my$1
class my$2
ncenerar
sumber
1 untuk observasi 3: misalnya, kita dapat melakukan Serializable s = new Serializable() {};(yang cukup berguna) dan jika ditandai ke kode Anda akan memberikan class my$3(atau apa pun melampirkan kelas dan nomor)
mengembalikan Monica - notmaynard
18

Anda dapat menjawab, hanya dalam satu baris

Tidak , Anda tidak pernah dapat instance Kelas Abstrak

Tapi, pewawancara masih belum setuju, maka Anda bisa memberitahunya

yang dapat Anda lakukan adalah, Anda dapat membuat Kelas Anonim.

Dan, menurut kelas Anonim, kelas dideklarasikan dan instantiate di tempat / baris yang sama

Jadi, mungkin saja pewawancara tertarik untuk memeriksa tingkat kepercayaan Anda dan seberapa banyak yang Anda ketahui tentang OOP.


sumber
17

Bagian teknis telah tercakup dengan baik dalam jawaban lainnya, dan sebagian besar berakhir dengan:
"Dia salah, dia tidak tahu apa-apa, minta dia untuk bergabung dengan SO dan menyelesaikan semuanya :)"

Saya ingin membahas fakta (yang telah disebutkan dalam jawaban lain) bahwa ini mungkin menjadi pertanyaan yang menegangkan dan merupakan alat penting bagi banyak pewawancara untuk mengetahui lebih banyak tentang Anda dan bagaimana Anda bereaksi terhadap situasi yang sulit dan tidak biasa. Dengan memberi Anda kode yang salah, dia mungkin ingin melihat apakah Anda membantah. Untuk mengetahui apakah Anda memiliki kepercayaan diri untuk melawan senior Anda dalam situasi yang serupa dengan ini.

PS: Saya tidak tahu kenapa, tetapi saya merasa pewawancara sudah membaca posting ini.

Mixcels
sumber
13

Kelas abstrak tidak bisa dipakai, tetapi mereka bisa subkelas. Lihat Tautan Ini

Contoh terbaik adalah

Meskipun kelas Calender memiliki metode abstrak getInstance () , tetapi ketika Anda mengatakannyaCalendar calc=Calendar.getInstance();

calc merujuk pada instance kelas dari kelas GregorianCalendar sebagai "GregorianCalendar extends Calendar "

Infact jenis batin annonymous memungkinkan Anda untuk membuat subclass tidak ada nama dari kelas abstrak dan sebuah contoh dari ini.

Abhishek_Mishra
sumber
11

Jawaban Teknis

Kelas abstrak tidak dapat dipakai - ini adalah dengan definisi dan desain.

Dari JLS, Bab 8. Kelas:

Kelas bernama dapat dinyatakan abstrak (§8.1.1.1) dan harus dinyatakan abstrak jika tidak diimplementasikan secara lengkap; kelas seperti itu tidak dapat dipakai, tetapi dapat diperpanjang dengan subclass.

Dari JSE 6 java doc untuk Classes.newInstance ():

InstantiationException - jika Kelas ini mewakili kelas abstrak, antarmuka, kelas array, tipe primitif, atau batal; atau jika kelas tidak memiliki konstruktor nullary; atau jika instantiasi gagal karena alasan lain.

Anda bisa, tentu saja, instantiate subclass konkret dari kelas abstrak (termasuk subclass anonim) dan juga melakukan typecast dari objek referensi ke tipe abstrak.

Sudut Berbeda tentang Ini - Teamplay & Kecerdasan Sosial:

Kesalahpahaman teknis semacam ini sering terjadi di dunia nyata ketika kita berurusan dengan teknologi kompleks dan spesifikasi legalistik.

"Keterampilan Orang" bisa lebih penting di sini daripada "Keterampilan Teknis". Jika secara kompetitif dan agresif mencoba membuktikan sisi argumen Anda, maka Anda secara teori bisa benar, tetapi Anda juga bisa melakukan lebih banyak kerusakan dalam bertarung / merusak "wajah" / menciptakan musuh daripada nilainya. Rekonsiliasi dan pengertianlah dalam menyelesaikan perbedaan Anda. Siapa tahu - mungkin Anda berdua "benar" tetapi bekerja sedikit berbeda makna istilah?

Siapa tahu - meski tidak mungkin, pewawancara mungkin sengaja membuat konflik kecil / kesalahpahaman untuk menempatkan Anda dalam situasi yang menantang dan melihat bagaimana Anda berperilaku secara emosional dan sosial. Bersikap ramah dan konstruktif dengan kolega, ikuti saran dari senior, dan ikuti setelah wawancara untuk menyelesaikan setiap tantangan / kesalahpahaman - melalui email atau panggilan telepon. Menunjukkan Anda termotivasi dan berorientasi pada detail.

Glen Best
sumber
7

Ini adalah fakta mapan yang tidakabstract class bisa diturunkan sebagai orang menjawab.

Ketika program mendefinisikan kelas anonim, kompiler sebenarnya menciptakan kelas baru dengan nama yang berbeda (memiliki pola di EnclosedClassName$nmana nnomor kelas anonim)

Jadi jika Anda mendekompilasi kelas Java ini, Anda akan menemukan kode seperti di bawah ini:

kelasku

abstract class my { 
    public void mymethod() 
    { 
        System.out.print("Abstract"); 
    }
} 

poly $ 1.class (kelas yang dihasilkan dari "kelas anonim")

class poly$1 extends my 
{
} 

ploly.cass

public class poly extends my
{
    public static void main(String[] a)
    {
        my m = new poly.1(); // instance of poly.1 class NOT the abstract my class

        m.mymethod();
    }
}
ITech
sumber
4

Tidak, Anda tidak dapat membuat instan kelas abstrak. Kami hanya instantiate kelas anonim. Dalam kelas abstrak kami mendeklarasikan metode abstrak dan mendefinisikan metode konkret saja.

vikas agrahari
sumber
4

Tentang Kelas Abstrak

  • Tidak dapat membuat objek dari kelas abstrak
  • Dapat membuat variabel (bisa berperilaku seperti tipe data)
  • Jika seorang anak tidak dapat mengesampingkan setidaknya satu metode abstrak dari orang tua, maka anak juga menjadi abstrak
  • Kelas abstrak tidak berguna tanpa kelas anak

Tujuan dari kelas abstrak adalah untuk berperilaku seperti basis. Dalam hierarki warisan Anda akan melihat kelas-kelas abstrak di bagian atas.

Priyankara
sumber
3

Anda dapat mengatakan:
kami tidak dapat membuat instance kelas abstrak, tetapi kami dapat menggunakan newkata kunci untuk membuat instance kelas anonim dengan hanya menambahkan {}sebagai badan implement di akhir kelas abstrak.

Eddy
sumber
3

Memperluas kelas tidak berarti Anda membuat instance kelas. Sebenarnya, dalam kasus Anda, Anda membuat instance dari subclass.

Saya cukup yakin bahwa kelas abstrak tidak memungkinkan memulai. Jadi, saya katakan tidak: Anda tidak dapat membuat instance kelas abstrak. Tapi, Anda bisa memperpanjang / mewarisinya.

Anda tidak dapat langsung membuat kelas abstrak. Tetapi itu tidak berarti bahwa Anda tidak bisa mendapatkan turunan kelas (bukan turunan kelas turunan abstrak asli) secara tidak langsung. Maksud saya Anda tidak dapat membuat instance kelas abstrak orginial, tetapi Anda dapat:

  1. Buat kelas kosong
  2. Mewarisi dari kelas abstrak
  3. Instantiate kelas yang direndahkan

Jadi Anda mendapatkan akses ke semua metode dan properti di kelas abstrak melalui turunan kelas turunan.

Kas
sumber
2

Tidak mungkin untuk membuat instance kelas abstrak. Apa yang benar-benar dapat Anda lakukan, telah menerapkan beberapa metode umum dalam kelas abstrak dan membiarkan yang lain tidak diimplementasikan (menyatakannya abstrak) dan membiarkan beton turun mengimplementasikannya tergantung pada kebutuhan mereka. Kemudian Anda dapat membuat pabrik, yang mengembalikan instance dari kelas abstrak ini (sebenarnya implementernya). Di pabrik Anda kemudian memutuskan, pelaksana mana yang harus dipilih. Ini dikenal sebagai pola desain pabrik:

   public abstract class AbstractGridManager {
        private LifecicleAlgorithmIntrface lifecicleAlgorithm;
        // ... more private fields

        //Method implemented in concrete Manager implementors 
        abstract public Grid initGrid();

        //Methods common to all implementors
        public Grid calculateNextLifecicle(Grid grid){
            return this.getLifecicleAlgorithm().calculateNextLifecicle(grid);
        }

        public LifecicleAlgorithmIntrface getLifecicleAlgorithm() {
            return lifecicleAlgorithm;
        }
        public void setLifecicleAlgorithm(LifecicleAlgorithmIntrface lifecicleAlgorithm) {
            this.lifecicleAlgorithm = lifecicleAlgorithm;
        }
        // ... more common logic and getters-setters pairs
    }

Implementer konkret hanya perlu mengimplementasikan metode yang dinyatakan sebagai abstrak, tetapi akan memiliki akses ke logika yang diimplementasikan di kelas-kelas dalam kelas abstrak, yang tidak dinyatakan abstrak:

public class FileInputGridManager extends AbstractGridManager {

private String filePath;

//Method implemented in concrete Manager implementors 
abstract public Grid initGrid();

public class FileInputGridManager extends AbstractGridManager {

    private String filePath;

    //Method implemented in concrete Manager implementors 
    abstract public Grid initGrid();

    public Grid initGrid(String filePath) {
        List<Cell> cells = new ArrayList<>();
        char[] chars;
        File file = new File(filePath); // for example foo.txt
        // ... more logic
        return grid;
    }
}

Lalu akhirnya pabrik terlihat seperti ini:

public class GridManagerFactory {
    public static AbstractGridManager getGridManager(LifecicleAlgorithmIntrface lifecicleAlgorithm, String... args){
        AbstractGridManager manager = null;

        // input from the command line
        if(args.length == 2){
            CommandLineGridManager clManager = new CommandLineGridManager();
            clManager.setWidth(Integer.parseInt(args[0]));
            clManager.setHeight(Integer.parseInt(args[1]));
            // possibly more configuration logic
            ...
            manager = clManager;
        } 
        // input from the file
        else if(args.length == 1){
            FileInputGridManager fiManager = new FileInputGridManager();
            fiManager.setFilePath(args[0]);
            // possibly more method calls from abstract class
            ...
            manager = fiManager ;
        }
        //... more possible concrete implementors
        else{
            manager = new CommandLineGridManager();
        }
        manager.setLifecicleAlgorithm(lifecicleAlgorithm);
        return manager;
    }
}

Penerima AbstractGridManager akan memanggil metode pada dirinya dan mendapatkan logika, diimplementasikan dalam penurunan konkret (dan sebagian dalam metode kelas abstrak) tanpa mengetahui apa implementasi konkret yang ia dapatkan. Ini juga dikenal sebagai inversi kontrol atau injeksi ketergantungan.

Picrochole
sumber
2

Tidak, kami tidak dapat membuat objek kelas abstrak, tetapi membuat variabel referensi dari kelas abstrak. Variabel referensi digunakan untuk merujuk pada objek dari kelas turunan (Sub kelas dari kelas Abstrak)

Berikut adalah contoh yang menggambarkan konsep ini

abstract class Figure { 

    double dim1; 

    double dim2; 

    Figure(double a, double b) { 

        dim1 = a; 

        dim2 = b; 

    } 

    // area is now an abstract method 

    abstract double area(); 

    }


    class Rectangle extends Figure { 
        Rectangle(double a, double b) { 
        super(a, b); 
    } 
    // override area for rectangle 
    double area() { 
        System.out.println("Inside Area for Rectangle."); 
        return dim1 * dim2; 
    } 
}

class Triangle extends Figure { 
    Triangle(double a, double b) { 
        super(a, b); 
    } 
    // override area for right triangle 
    double area() { 
        System.out.println("Inside Area for Triangle."); 
        return dim1 * dim2 / 2; 
    } 
}

class AbstractAreas { 
    public static void main(String args[]) { 
        // Figure f = new Figure(10, 10); // illegal now 
        Rectangle r = new Rectangle(9, 5); 
        Triangle t = new Triangle(10, 8); 
        Figure figref; // this is OK, no object is created 
        figref = r; 
        System.out.println("Area is " + figref.area()); 
        figref = t; 
        System.out.println("Area is " + figref.area()); 
    } 
}

Di sini kita melihat bahwa kita tidak dapat membuat objek tipe Gambar tetapi kita dapat membuat variabel referensi tipe Gambar. Di sini kita membuat variabel referensi dari tipe Gambar dan Gambar. Variabel referensi kelas digunakan untuk merujuk ke objek Class Rectangle dan Triangle.

Ketan G
sumber
0

Sebenarnya kita tidak bisa membuat objek kelas abstrak secara langsung. Apa yang kita buat adalah variabel referensi panggilan abstrak. Variabel referensi digunakan untuk merujuk ke objek kelas yang mewarisi kelas abstrak yaitu subkelas dari kelas abstrak.

Jency
sumber