java - কিভাবে জাভা একটি জেনেরিক অ্যারে তৈরি করতে?




arrays generics (20)

জাভা জেনেরিক্স বাস্তবায়নের কারণে, আপনি এইরকম কোড পাবেন না:

public class GenSet<E> {
    private E a[];

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
    }
}

টাইপ নিরাপত্তা বজায় রাখার সময় আমি কিভাবে এই বাস্তবায়ন করতে পারি?

আমি জাভা ফোরামে এমন একটি সমাধান দেখেছি যা এই রকম হয়:

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

কিন্তু আমি সত্যিই কি ঘটছে তা পাবেন না।


অন্যান্য ব্যক্তিদের দ্বারা প্রস্তাবিত বাধ্যতামূলক কাস্ট অবৈধ কাটিং ব্যতিক্রম ছাড়াও আমার জন্য কাজ করে নি।

যাইহোক, এই নিখুঁত কাস্ট জরিমানা কাজ করেছেন:

Item<K>[] array = new Item[SIZE];

যেখানে আইটেম একটি শ্রেণী আমি সংজ্ঞায়িত সদস্য ধারণকারী:

private K value;

এইভাবে আপনি টাইপের একটি অ্যারে পাবেন (যদি আইটেমটিতে কেবল মান থাকে) অথবা কোনও জেনেরিক টাইপ যা আপনি শ্রেণী আইটেমে সংজ্ঞায়িত করতে চান।


আপনার পোস্ট করা উদাহরণটিতে কী চলছে তার কোনও প্রশ্নের উত্তর অন্য কেউ করেনি।

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

অন্যরা বলেছে জেনেরিক্স সংকলনের সময় "মুছে ফেলা" হয়। সুতরাং runtime এ একটি জেনেরিক একটি উদাহরণ তার কম্পোনেন্ট টাইপ কি জানেন না। এর কারণ ঐতিহাসিক, সূর্য বিদ্যমান ইন্টারফেস (উৎস এবং বাইনারি উভয়) ভঙ্গ করে জেনেরিক্স যোগ করতে চেয়েছিল।

অন্যদিকে অ্যারে রানটাইম এ তাদের উপাদান টাইপ জানি।

এই উদাহরণটি কনস্ট্রাক্টর (যা টাইপ জানে) পাস করে এমন কোডটি রেখে সমস্যাটির সমাধান করে।

তাই অ্যাপ্লিকেশন ভালো কিছু সঙ্গে ক্লাস গঠন করবে

Stack<foo> = new Stack<foo>(foo.class,50)

এবং কনস্ট্রাক্টর এখন জানেন (রানটাইমে) কম্পোনেন্ট টাইপ কি এবং সেই তথ্যটি প্রতিফলন API এর মাধ্যমে অ্যারে গঠন করতে ব্যবহার করতে পারে।

Array.newInstance(clazz, capacity);

অবশেষে আমাদের একটি টাইপ কাস্ট আছে কারণ কম্পাইলারের কোনও উপায় জানা নেই যে Array#newInstance() দ্বারা ফেরত আসা অ্যারের সঠিক টাইপ (যদিও আমরা জানি)।

এই শৈলীটি কিছুটা কুৎসিত তবে কখনও কখনও জেনারিক প্রকার তৈরি করার জন্য এটি কমপক্ষে খারাপ সমাধান হতে পারে যা কোনও কারণের জন্য রানটাইম এ তাদের উপাদান টাইপটি জানতে হবে (অ্যারে তৈরি করা, বা তাদের উপাদান প্রকারের উদাহরণগুলি ইত্যাদি তৈরি করা)।


আপনি একটি কাস্ট ব্যবহার করতে পারেন:

public class GenSet<Item> {
    private Item[] a;

    public GenSet(int s) {
        a = (Item[]) new Object[s];
    }
}

আপনি কনস্ট্রাক্টর ক্লাস আর্গুমেন্ট পাস করতে হবে না। এটা চেষ্টা কর.

static class GenSet<T> {
    private final T[] array;
    @SuppressWarnings("unchecked")
    public GenSet(int capacity, T... dummy) {
        if (dummy.length > 0)
            throw new IllegalArgumentException(
              "Do not provide values for dummy argument.");
        Class<?> c = dummy.getClass().getComponentType();
        array = (T[])Array.newInstance(c, capacity);
    }
    @Override
    public String toString() {
        return "GenSet of " + array.getClass().getComponentType().getName()
            + "[" + array.length + "]";
    }
}

এবং

GenSet<Integer> intSet = new GenSet<>(3);
System.out.println(intSet);
System.out.println(new GenSet<String>(2));

ফলাফল:

GenSet of java.lang.Integer[3]
GenSet of java.lang.String[2]

আমি আসলে একটি জেনেরিক অ্যারে আরম্ভ অক্ষমতা অস্তিত্ব একটি চমত্কার অনন্য সমাধান খুঁজে পাওয়া যায় নি। আপনাকে যা করতে হবে তা জেনারিক ভেরিয়েবল টিতে এমন একটি বর্গ তৈরি করে যাতে করে:

class GenericInvoker <T> {
    T variable;
    public GenericInvoker(T variable){
        this.variable = variable;
    }
}

এবং তারপরে আপনার অ্যারের ক্লাসে এটির মতো শুরু করুন:

GenericInvoker<T>[] array;
public MyArray(){
    array = new GenericInvoker[];
}

একটি new Generic Invoker[] শুরু করা new Generic Invoker[] অচেনা অবস্থায় একটি সমস্যা সৃষ্টি করবে তবে আসলে কোন সমস্যা নেই।

অ্যারের থেকে আপনি অ্যারে [i]। এটিকে কল করতে হবে:

public T get(int index){
    return array[index].variable;
}

অ্যারের আকার পরিবর্তন করার মতো বাকিগুলি Arrays.copyOf () এর মতো করা যেতে পারে:

public void resize(int newSize){
    array = Arrays.copyOf(array, newSize);
}

এবং যোগ ফাংশন তাই যোগ করা যেতে পারে:

public boolean add(T element){
    // the variable size below is equal to how many times the add function has been called 
    // and is used to keep track of where to put the next variable in the array
    arrays[size] = new GenericInvoker(element);
    size++;
}

আমি এই কোড একটি কার্যকর জেনেরিক অ্যারে তৈরি হবে কিনা ভাবছি?

public T [] createArray(int desiredSize){
    ArrayList<T> builder = new ArrayList<T>();
    for(int x=0;x<desiredSize;x++){
        builder.add(null);
    }
    return builder.toArray(zeroArray());
}

//zeroArray should, in theory, create a zero-sized array of T
//when it is not given any parameters.

private T [] zeroArray(T... i){
    return i;
}

সম্পাদনা: সম্ভবত এমন একটি অ্যারে তৈরি করার বিকল্প উপায়, যদি আপনার প্রয়োজনীয় আকারটি ছোট এবং ছোট ছিল, তবে কেবলমাত্র প্রয়োজনীয় সংখ্যক "নাল" গুলিটি zeroArray কমান্ডটি খেতে হবে?

যদিও স্পষ্টভাবে এটি createArray কোড ব্যবহার করে বহুমুখী নয়।


আমি এই সমস্যা প্রায় একটি কাজ একটি সাজানোর পাওয়া।

নিচের লাইনটি জেনেরিক অ্যারে সৃষ্টি ত্রুটি ফেলে দেয়

List<Person>[] personLists=new ArrayList<Person>()[10];

তবে যদি আমি একটি পৃথক শ্রেণীতে List<Person> , এটি কাজ করে।

import java.util.ArrayList;
import java.util.List;


public class PersonList {

    List<Person> people;

    public PersonList()
    {
        people=new ArrayList<Person>();
    }
}

আপনি একটি মেজাজ মাধ্যমে ব্যক্তি ব্যক্তিত্ব শ্রেণীকক্ষে প্রকাশ করতে পারেন। নিচের লাইনটি আপনাকে একটি অ্যারে দেবে যা প্রত্যেকটি List<Person> একটি List<Person> । অন্যান্য শব্দ অ্যারে List<Person>

PersonList[] personLists=new PersonList[10];

আমার এমন কিছু কোডের দরকার ছিল যা আমি কাজ করছিলাম এবং আমি এটি কাজ করার জন্য যা করেছি। এখন পর্যন্ত কোন সমস্যা নেই।


আমি একটি প্রশ্ন জিজ্ঞাসা করতে হবে: আপনার GenSet "চেক করা" বা "অচেনা"? ওটার মানে কি?

  • চেক করা : শক্তিশালী টাইপিংGenSet স্পষ্টভাবে জানেন যে এটি কোন ধরণের বস্তু রয়েছে (অর্থাৎ তার GenSet Class<E> যুক্তি দিয়ে স্পষ্টভাবে বলা হয় এবং যখন E -প্রকারের আর্গুমেন্টগুলি পাস করা হয় তখন পদ্ধতিগুলি ব্যতিক্রমকে GenSet করবে। Collections.checkedCollection দেখুন। চেক করা।

    -> যে ক্ষেত্রে, আপনি লিখতে হবে:

    public class GenSet<E> {
    
        private E[] a;
    
        public GenSet(Class<E> c, int s) {
            // Use Array native method to create array
            // of a type only known at run time
            @SuppressWarnings("unchecked")
            final E[] a = (E[]) Array.newInstance(c, s);
            this.a = a;
        }
    
        E get(int i) {
            return a[i];
        }
    }
  • অচেনা : দুর্বল টাইপিং । কোন টাইপ পরীক্ষণ আসলে যুক্তি হিসাবে পাস বস্তুর কোনো সম্পন্ন করা হয়।

    -> যে ক্ষেত্রে, আপনি লিখতে হবে

    public class GenSet<E> {
    
        private Object[] a;
    
        public GenSet(int s) {
            a = new Object[s];
        }
    
        E get(int i) {
            @SuppressWarnings("unchecked")
            final E e = (E) a[i];
            return e;
        }
    }

    উল্লেখ্য যে অ্যারের উপাদান টাইপটি টাইপ পরামিতির erasure হওয়া উচিত:

    public class GenSet<E extends Foo> { // E has an upper bound of Foo
    
        private Foo[] a; // E erases to Foo, so use Foo[]
    
        public GenSet(int s) {
            a = new Foo[s];
        }
    
        ...
    }

জাভাতে জেনেরিক্সের জ্ঞাত, পরিচিত এবং ইচ্ছাকৃত, দুর্বলতা থেকে এটি সমস্ত ফলাফল: এটি ক্ষয় ব্যবহার করে প্রয়োগ করা হয়েছিল, তাই "জেনেরিক" ক্লাসগুলি জানে না যে তারা রান টাইম সহ কোন ধরনের যুক্তি তৈরি করেছিল এবং তাই টাইপ- নিরাপত্তা কিছু সুনির্দিষ্ট প্রক্রিয়া (টাইপ পরীক্ষণ) প্রয়োগ করা হয় না।


আসলে এটি করার একটি সহজ উপায়, বস্তুর একটি অ্যারে তৈরি করা এবং নিম্নলিখিত উদাহরণের মতো আপনার পছন্দসই প্রকারে ঢোকানো:

T[] array = (T[])new Object[SIZE];

যেখানে SIZE একটি ধ্রুবক এবং T একটি টাইপ শনাক্তকারী


উদাহরণ একটি অ্যারে তৈরি করতে জাভা প্রতিচ্ছবি ব্যবহার করা হয়। এটি সাধারণত টাইপসফাইফ না থাকলে এটি করার প্রস্তাব দেওয়া হয় না। পরিবর্তে, আপনি কি করতে হবে শুধু একটি অভ্যন্তরীণ তালিকা ব্যবহার করুন, এবং এ্যারে এড়ানোর এড়ানোর।


এই কোডটি দেখুন:

public static <T> T[] toArray(final List<T> obj) {
    if (obj == null || obj.isEmpty()) {
        return null;
    }
    final T t = obj.get(0);
    final T[] res = (T[]) Array.newInstance(t.getClass(), obj.size());
    for (int i = 0; i < obj.size(); i++) {
        res[i] = obj.get(i);
    }
    return res;
}

এটি কোনও বস্তুর একটি তালিকা একই ধরণের একটি অ্যারে রূপান্তর করে।


এই সমাধান সম্পর্কে কি?

@SafeVarargs
public static <T> T[] toGenericArray(T ... elems) {
    return elems;
}

এটি কাজ করে এবং সত্য হতে খুব সহজ দেখায়। কোন অসুবিধা আছে?


এটা চেষ্টা কর.

private int m = 0;
private int n = 0;
private Element<T>[][] elements = null;

public MatrixData(int m, int n)
{
    this.m = m;
    this.n = n;

    this.elements = new Element[m][n];
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            this.elements[i][j] = new Element<T>();
        }
    }
}

এটি কার্যকর জাভা, দ্বিতীয় সংস্করণ , আইটেম 25 এর অধ্যায় 5 (জেনেরিক্স) -এ আচ্ছাদিত ... তালিকাগুলি অ্যারে পছন্দ করে

আপনার কোড কাজ করবে, যদিও এটি একটি অচেনা সতর্কতা তৈরি করবে (যা আপনি নিম্নলিখিত টীকাটি দিয়ে দমন করতে পারেন:

@SuppressWarnings({"unchecked"})

তবে, সম্ভবত এটি একটি অ্যারের পরিবর্তে একটি তালিকা ব্যবহার করা ভাল হবে।

OpenJDK প্রকল্প সাইটে এই বাগ / বৈশিষ্ট্যটির একটি আকর্ষণীয় আলোচনা রয়েছে।


জাভা জেনেরিকগুলি কম্পাইল সময়গুলিতে এবং যথাযথ কাস্টগুলি সন্নিবেশ করাতে পরীক্ষা করে কাজ করে, তবে সংকলিত ফাইলগুলিতে প্রকারগুলি মুছে ফেলছে। এটি জেনেরিক লাইব্রেরিগুলিকে কোড দ্বারা ব্যবহারযোগ্য করে তোলে যা জেনেরিকস (যা ইচ্ছাকৃত ডিজাইন সিদ্ধান্ত ছিল না) বোঝে না তবে এর অর্থ হল আপনি কীভাবে টাইপ চলছে তা সাধারণত খুঁজে পাবেন না।

পাবলিক Stack(Class<T> clazz,int capacity) কনস্ট্রাক্টর আপনাকে রান সময় সময়ে ক্লাস অবজেক্টটি পাস করতে হবে, যার অর্থ ক্লাসের তথ্য রানটাইম এ কোডের জন্য প্রয়োজনীয়। এবং Class<T> ফর্মটি মানে যে কম্পাইলারটি পরীক্ষা করবে যে আপনি যে শ্রেণী বস্তুটি পাস করেছেন তা অবশ্যই টি টাইপের জন্য ক্লাস অবজেক্ট টি। T এর উপশ্রেণী নয়, T এর একটি উপশ্রেণী নয়, তবে সঠিকভাবে টি।

এর অর্থ হল আপনি আপনার কন্সট্রকটারে যথাযথ ধরনের একটি অ্যারে অবজেক্ট তৈরি করতে পারেন, যার মানে আপনি যে সংগ্রহগুলি আপনার সংগ্রহস্থলে সঞ্চয় করেন তার ধরনগুলি তাদের সংগ্রহের সাথে যুক্ত করা পয়েন্টে চেক করা হবে।


টাইপ নিরাপত্তার সময় আপনি যা খুঁজছিলেন তা সঠিকভাবে সন্ধান করার জন্য জেনেরিক্সটি কীভাবে ব্যবহার করবেন তা এখানে দেখুন (অন্য উত্তরগুলির বিরোধিতা করে, যা আপনাকে একটি Object অ্যারে ফিরিয়ে দেবে বা সংকলন সময়ে সতর্কতার ফলাফল দেবে):

import java.lang.reflect.Array;  

public class GenSet<E> {  
    private E[] a;  

    public GenSet(Class<E[]> clazz, int length) {  
        a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));  
    }  

    public static void main(String[] args) {  
        GenSet<String> foo = new GenSet<String>(String[].class, 1);  
        String[] bar = foo.a;  
        foo.a[0] = "xyzzy";  
        String baz = foo.a[0];  
    }  
}

GenSet সতর্কতা ছাড়াই সংকলন করে এবং আপনি main দেখতে পারেন যেহেতু আপনি যে কোনও প্রকারের GenSet উদাহরণ হিসাবে ঘোষণা করেন, আপনি GenSet একটি অ্যারের জন্য একটি বরাদ্দ করতে পারেন এবং আপনি GenSet a ধরনের পরিবর্তনশীল থেকে a উপাদান বরাদ্দ করতে পারেন। অর্থাত্ যে অ্যারের অ্যারের এবং মান সঠিক টাইপ হয়।

এটি জাভা টিউটোরিয়ালগুলিতে আলোচনা হিসাবে রান টাইম টোকেন হিসাবে ক্লাস লিখন ব্যবহার করে কাজ করে। ক্লাস java.lang.Class উদাহরণ হিসাবে কম্পাইলার দ্বারা চিকিত্সা করা হয়। এক ব্যবহার করার জন্য, ক্লাসের নামের সাথে কেবল ক্লাসের নাম অনুসরণ করুন। সুতরাং, String.class ক্লাস String প্রতিনিধিত্ব করে একটি Class অবজেক্ট হিসাবে কাজ করে। এটি ইন্টারফেস, enums, কোনও মাত্রিক অ্যারে (উদাহরণস্বরূপ String[].class ), primitives (যেমন int.class ), এবং কীওয়ার্ড void (অর্থাত void.class ) জন্য কাজ করে।

Class নিজেই জেনেরিক ( Class<T> হিসাবে ঘোষণা করা হয়, যেখানে T Class জন্য স্ট্যাটাস উপস্থাপিত হয় এমন ধরনের ধরণ), যার অর্থ String.class ধরন হল Class<String>

সুতরাং, যখনই আপনি GenSet জন্য GenSet কল করেন, তখন GenSet ইনস্ট্যান্সের ঘোষিত ধরন (যেমন String[].class GenSet<String> -এর GenSet<String> প্রতিনিধিত্বকারী প্রথম যুক্তিটির জন্য আপনি ক্লাসের আক্ষরিক অর্থে পাস করেন। মনে রাখবেন যে আপনি প্রাইমিটাইভগুলির একটি অ্যারে পেতে পারবেন না, কারণ প্রাইমাইটিভগুলি টাইপ ভেরিয়েবলগুলির জন্য ব্যবহার করা যাবে না।

কনস্ট্রাক্টরের ভিতর, পদ্ধতিটি কল করে পাঠানো বস্তুটি Class বস্তুর প্রতিনিধিত্ব করে শ্রেণিতে উপস্থাপন করে যা পদ্ধতিটি বলা হয়। স্ট্যাটিক মেথডকে java.lang.reflect.Array newInstance হয়। অ্যারে একটি Object হিসাবে ফেরত newInstance Object দ্বারা উপস্থাপিত প্রকারের অ্যারে, প্রথম যুক্তি এবং দ্বিতীয় যুক্তি হিসাবে প্রেরিত int দ্বারা নির্দিষ্ট দৈর্ঘ্যের হিসাবে প্রেরিত। পদ্ধতিটি কল getComponentType Class getComponentType উপস্থাপন করে এমন Class বস্তুর প্রতিনিধিত্বকারী অ্যারের উপাদান getComponentType প্রতিনিধিত্ব করে, যা পদ্ধতিতে বলা হয়েছিল (উদাহরণস্বরূপ String.class String[].class , Class বস্তু কোন অ্যারের প্রতিনিধিত্ব করে না, ।

যে শেষ বাক্য সম্পূর্ণরূপে সঠিক নয়। কলিং String[].class.getComponentType() Class String[].class.getComponentType() প্রতিনিধিত্ব করে একটি Class অবজেক্ট প্রদান করে, তবে তার প্রকার Class<?> , Class<String> , তাই আপনি নীচের মতো কিছু করতে পারবেন না।

String foo = String[].class.getComponentType().cast("bar"); // won't compile

Class প্রতিটি বস্তুর জন্য একই Class যা Class অবজেক্ট প্রদান করে।

এই প্রশ্নের জয়াচিম সোয়ারের মন্তব্য সম্পর্কে (আমার নিজের উপর মন্তব্য করার জন্য আমার কাছে যথেষ্ট খ্যাতি নেই), T[] কাস্ট ব্যবহার করে উদাহরণটি একটি সতর্কতার ফলস্বরূপ হবে কারণ কম্পাইলার সেই ক্ষেত্রে টাইপ নিরাপত্তা নিশ্চিত করতে পারে না।

ইনগো এর মন্তব্য সম্পর্কে সম্পাদনা করুন:

public static <T> T[] newArray(Class<T[]> type, int size) {
   return type.cast(Array.newInstance(type.getComponentType(), size));
}

মূল্য তালিকা একটি তালিকা ...

public <T> T[] array(T... values) {
    return values;
}

সম্ভবত এই প্রশ্নটির সাথে সম্পর্কযুক্ত কিন্তু যখন আমি " generic array creation " ত্রুটিটি ব্যবহার করার জন্য ত্রুটি পেয়েছিলাম

Tuple<Long,String>[] tupleArray = new Tuple<Long,String>[10];

আমি @SuppressWarnings({"unchecked"}) দিয়ে নিম্নলিখিত কাজগুলি খুঁজে বের করেছি (এবং আমার জন্য কাজ করেছেন @SuppressWarnings({"unchecked"}) :

 Tuple<Long, String>[] tupleArray = new Tuple[10];

জেনারিক অ্যারে সৃষ্টি জাভা নিষিদ্ধ কিন্তু আপনি এটি করতে পারেন

class Stack<T> {
private final T[] array;
public Stack(int capacity) {
    array = (T[]) new Object[capacity];
 }
}

private E a[];
private int size;

public GenSet(int elem)
{
    size = elem;
    a = (E[]) new E[size];
}




instantiation