একটি জাভা ক্লাস কীভাবে তৈরি করা যায় যা দুটি জেনেরিক ধরণের সাথে একটি ইন্টারফেস প্রয়োগ করে?


164

আমার একটি জেনেরিক ইন্টারফেস রয়েছে

public interface Consumer<E> {
    public void consume(E e);
}

আমার একটি ক্লাস রয়েছে যা দুটি ধরণের জিনিস খায়, তাই আমি এর মতো কিছু করতে চাই:

public class TwoTypesConsumer implements Consumer<Tomato>, Consumer<Apple>
{
   public void consume(Tomato t) {  .....  }
   public void consume(Apple a) { ...... }
}

স্পষ্টতই আমি এটি করতে পারি না।

আমি অবশ্যই প্রেরণটি নিজেই প্রয়োগ করতে পারি, যেমন

public class TwoTypesConsumer implements Consumer<Object> {
   public void consume(Object o) {
      if (o instanceof Tomato) { ..... }
      else if (o instanceof Apple) { ..... }
      else { throw new IllegalArgumentException(...) }
   }
}

তবে আমি জেনেরিকগুলি সংকলন-টাইপ টাইপ-চেকিং এবং প্রেরণের সমাধানের সন্ধান করছি।

আমি সবচেয়ে ভাল সমাধানটি ভাবতে পারি তা হল পৃথক ইন্টারফেসগুলি সংজ্ঞায়িত করা, যেমন eg

public interface AppleConsumer {
   public void consume(Apple a);
}

কার্যত, এই সমাধানটি ঠিক আছে, আমি মনে করি। এটি কেবল ভার্জোজ এবং কুরুচিপূর্ণ।

কোন ধারনা?


কেন আপনার একই বেসটিপের দুটি জেনেরিক ইন্টারফেসের প্রয়োজন?
আকারনোকড

6
টাইপ ইরেজরের কারণে আপনি এটি করতে পারবেন না। এটি দুটি পৃথক শ্রেণি রাখুন যা গ্রাহককে কার্যকর করে। আরও ছোট ক্লাস তৈরি করে তবে আপনার কোডটি জেনেরিক রাখে (স্বীকৃত উত্তরটি ব্যবহার করবেন না, এটি পুরো ধারণাটি ভেঙে দেয় ... আপনি টুটাইপস কনসুমারকে গ্রাহক হিসাবে চিকিত্সা করতে পারবেন না, যা বিএডি)।
লুইস ডায়মন্ড

- কার্মিক শৈলী impl এই চেক করুন stackoverflow.com/a/60466413/4121845
mano_ksp

উত্তর:


78

এনক্যাপসুলেশন বিবেচনা করুন:

public class TwoTypesConsumer {
    private TomatoConsumer tomatoConsumer = new TomatoConsumer();
    private AppleConsumer appleConsumer = new AppleConsumer();

    public void consume(Tomato t) { 
        tomatoConsumer.consume(t);
    }

    public void consume(Apple a) { 
        appleConsumer.consume(a);
    }

    public static class TomatoConsumer implements Consumer<Tomato> {
        public void consume(Tomato t) {  .....  }
    }

    public static class AppleConsumer implements Consumer<Apple> {
        public void consume(Apple a) {  .....  }
    }
}

যদি এই স্থির অভ্যন্তরীণ ক্লাসগুলি তৈরি করা আপনাকে বিরক্ত করে, আপনি বেনাম শ্রেণি ব্যবহার করতে পারেন:

public class TwoTypesConsumer {
    private Consumer<Tomato> tomatoConsumer = new Consumer<Tomato>() {
        public void consume(Tomato t) {
        }
    };

    private Consumer<Apple> appleConsumer = new Consumer<Apple>() {
        public void consume(Apple a) {
        }
    };

    public void consume(Tomato t) {
        tomatoConsumer.consume(t);
    }

    public void consume(Apple a) {
        appleConsumer.consume(a);
    }
}

2
কোনওভাবে কোড টুথিকেশনটির মতো দেখতে দেখতে ... আমি একই সমস্যার মুখোমুখি হয়েছি এবং পরিষ্কার দেখতে পাই এমন কোনও সমাধান পাই নি।
bln-tom

109
কিন্তু কোনও চুক্তি TwoTypesConsumerপূরণ করে না , তাহলে কী? এটি কোনও পদ্ধতিতে পাস করা যায় না যা উভয় প্রকারেরই চায় Consumer। দ্বি-ধরণের ভোক্তার পুরো ধারণাটি হ'ল আপনি এটি এমন কোনও পদ্ধতিতে দিতে পারেন যা টমেটো ভোক্তা চায় এবং সেইসাথে কোনও অ্যাপল গ্রাহক চায় wants এখানে আমরা না।
জেফ অ্যাক্সেলরড

@ জেফএক্সেলরোড আমি অভ্যন্তরীণ ক্লাসগুলিকে অ-স্থির করে তুলব যাতে প্রয়োজনে তাদের ঘেরের উদাহরণগুলিতে অ্যাক্সেস TwoTypesConsumerথাকে এবং তারপরে আপনি twoTypesConsumer.getAppleConsumer()কোনও পদ্ধতিতে যেতে পারেন যা অ্যাপল গ্রাহক চায়। আরেকটি বিকল্প addConsumer(Producer<Apple> producer)হ'ল টুটাইপস কনসুমার এর অনুরূপ পদ্ধতি যুক্ত করা ।
Herman

যদি আপনার ইন্টারফেসের উপর নিয়ন্ত্রণ না থাকে তবে এটি কাজ করে না (উদাঃ সিএক্সএফ / আরএস ExceptionMapper) ...
ভাইকিংস্টিভ

17
আমি এটি বলব: এটি জাভা সহ একটি ত্রুটি । বাস্তবায়নগুলি বিভিন্ন যুক্তি গ্রহণ করে তবে আমাদের একই ইন্টারফেসের একাধিক বাস্তবায়ন করার অনুমতি দেওয়া উচিত নয় এর কোনও কারণ নেই।
gromit190

41

টাইপ ইরেজরের কারণে আপনি একই ইন্টারফেসটি দুইবার প্রয়োগ করতে পারবেন না (বিভিন্ন ধরণের পরামিতি সহ)।


6
আমি দেখতে পাচ্ছি এটি কীভাবে সমস্যা ... প্রশ্নটি হল এই সমস্যাটিকে বাইপাস করার সর্বোত্তম (সবচেয়ে দক্ষ, নিরাপদ, মার্জিত) উপায় কী।
daphshez

2
ব্যবসায়ের যুক্তিতে না গিয়ে এখানে কিছু ভিজিটর ধাঁচের মতো 'গন্ধযুক্ত'।
শিমি বান্দিল

12

স্টিভ ম্যাকলিডের একটিটির উপর ভিত্তি করে এখানে একটি সম্ভাব্য সমাধান রয়েছে :

public class TwoTypesConsumer {
    public void consumeTomato(Tomato t) {...}
    public void consumeApple(Apple a) {...}

    public Consumer<Tomato> getTomatoConsumer() {
        return new Consumer<Tomato>() {
            public void consume(Tomato t) {
                consumeTomato(t);
            }
        }
    }

    public Consumer<Apple> getAppleConsumer() {
        return new Consumer<Apple>() {
            public void consume(Apple a) {
                consumeApple(t);
            }
        }
    }
}

প্রশ্নের অন্তর্নিহিত প্রয়োজন ছিল Consumer<Tomato>এবং Consumer<Apple>অবজেক্টগুলি যা রাষ্ট্রকে ভাগ করে নেয়। Consumer<Tomato>, Consumer<Apple>বস্তুর প্রয়োজনীয়তা অন্যান্য পদ্ধতিগুলি থেকে আসে যা এগুলি পরামিতি হিসাবে প্রত্যাশা করে। রাষ্ট্র ভাগ করার জন্য আমার উভয়কে বাস্তবায়ন করার জন্য একটি শ্রেণির প্রয়োজন।

স্টিভের ধারণা ছিল দুটি অভ্যন্তর শ্রেণি ব্যবহার করা, যার প্রতিটি আলাদা জেনেরিক প্রকার প্রয়োগ করে।

এই সংস্করণটি গ্রাহক ইন্টারফেস প্রয়োগ করে এমন বস্তুর জন্য গেটার যুক্ত করে, যা তাদের প্রত্যাশা করে অন্যান্য পদ্ধতিতে যেতে পারে।


2
যদি কেউ এটি ব্যবহার করে: Consumer<*>উদাহরণস্বরূপ ক্ষেত্রগুলিতে get*Consumerপ্রায়শই বলা হয় তবে এটি উদাহরণগুলি সঞ্চয় করা উপযুক্ত ।
TWiStErRob

7

কমপক্ষে, আপনি নিম্নলিখিতটির মতো কিছু করে প্রেরণটির প্রয়োগের ক্ষেত্রে একটি ছোট উন্নতি করতে পারেন:

public class TwoTypesConsumer implements Consumer<Fruit> {

টমেটো এবং অ্যাপলের পূর্বপুরুষ হ'ল ফল।


14
ধন্যবাদ, তবে উপকারীরা যাই বলুক না কেন, আমি টমেটোকে ফল হিসাবে বিবেচনা করি না। দুর্ভাগ্যক্রমে অবজেক্ট ব্যতীত আর কোন সাধারণ বেস শ্রেণি নেই।
daphshez

2
আপনি সর্বদা নামের একটি ক্লাস তৈরি করতে পারেন: অ্যাপলঅরটোম্যাটো;)
শিমি বন্দিয়েল

1
আরও ভাল, একটি ফল যুক্ত করুন যা অ্যাপল বা টমেটো উভয়কেই প্রতিনিধিত্ব করে।
টম হাটিন - 19:40

@ টম: আপনি যা বলছেন তা যদি না বোঝা যায় তবে আপনার পরামর্শটি কেবল সমস্যাটিকে সামনে ঠেলে দেবে, যেহেতু ফল অ্যাপল বা টমেটো উভয়কেই অর্পণ করতে সক্ষম হবে, ফল অবশ্যই অ্যাপল এবং টমেটো উভয়েরই কাছে একটি সুপার ক্লাসের ক্ষেত্র থাকতে হবে এটি অর্পিত বস্তুর উল্লেখ করে ring
বুহব

1
এটি বোঝায় যে টুটাইপস কনসুমার যেকোন প্রকারের ফল গ্রহণ করতে পারে, বর্তমানে প্রয়োগ করা এবং যে কেউ ভবিষ্যতে প্রয়োগ করতে পারে।
টম গিলেন

3

শুধু এই হোঁচট খেয়েছি। এটি ঠিক ঘটেছে, আমারও একই সমস্যা ছিল, তবে আমি এটি অন্যভাবে সমাধান করেছি: আমি এই জাতীয় একটি নতুন ইন্টারফেস তৈরি করেছি

public interface TwoTypesConsumer<A,B> extends Consumer<A>{
    public void consume(B b);
}

দুর্ভাগ্যক্রমে, এটিকে সমস্ত যুক্তির বিপরীতে Consumer<A>হিসাবে বিবেচনা করা হয় NOT Consumer<B>সুতরাং আপনাকে নিজের শ্রেণীর অভ্যন্তরে দ্বিতীয় গ্রাহকের জন্য একটি ছোট অ্যাডাপ্টার তৈরি করতে হবে

public class ConsumeHandler implements TwoTypeConsumer<A,B>{

    private final Consumer<B> consumerAdapter = new Consumer<B>(){
        public void consume(B b){
            ConsumeHandler.this.consume(B b);
        }
    };

    public void consume(A a){ //...
    }
    public void conusme(B b){ //...
    }
}

যদি একটি Consumer<A>প্রয়োজন হয়, আপনি কেবল পাস করতে পারেন this, এবং যদি Consumer<B>প্রয়োজন হয় কেবল পাস করুনconsumerAdapter


ড্যাফনার জবাব একই, তবে ক্লিনার এবং কম সংশ্লেষিত।
TWiStErRob

1

জেনেরিক ধরণের এবং ডুপ্লিকেট ইন্টারফেস ঘোষণার কারণে নীচের শ্রেণীর সংজ্ঞাটি সংকলন করা যায় না আপনি এটি সরাসরি এক শ্রেণিতে করতে পারবেন না।

class TwoTypesConsumer implements Consumer<Apple>, Consumer<Tomato> { 
 // cannot compile
 ...
}

এক শ্রেণিতে একই গ্রাহক ক্রিয়াকলাপ প্যাকিংয়ের জন্য অন্য কোনও সমাধানের জন্য আপনার শ্রেণিটি নির্ধারণ করতে হবে:

class TwoTypesConsumer { ... }

উভয় ক্রিয়াকলাপের সংজ্ঞাটি পুনরায় / ডুপ্লিকেট করার জন্য এটি অর্থহীন এবং এগুলি ইন্টারফেস থেকে রেফারেন্স করা হবে না। আইএমএইচও এটি করা একটি খারাপ ছোট এবং কোডের নকল যা আমি এড়াতে চাইছি।

এটি একটি সূচকও হতে পারে যে একটি শ্রেণিতে 2 টি পৃথক বস্তু (যদি তারা সংযুক্ত না হয়) গ্রাস করার জন্য খুব বেশি দায়বদ্ধতা রয়েছে।

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

interface ConsumerFactory {
     Consumer<Apple> createAppleConsumer();
     Consumer<Tomato> createTomatoConsumer();
}

বাস্তবে যদি এই ধরণেরগুলি সত্যই সংযুক্ত হয় (সম্পর্কিত) তবে আমি এইভাবে একটি বাস্তবায়ন তৈরি করার পরামর্শ দেব:

class TwoTypesConsumerFactory {

    // shared objects goes here

    private class TomatoConsumer implements Consumer<Tomato> {
        public void consume(Tomato tomato) {
            // you can access shared objects here
        }
    }

    private class AppleConsumer implements Consumer<Apple> {
        public void consume(Apple apple) {
            // you can access shared objects here
        }
    }


    // It is really important to return generic Consumer<Apple> here
    // instead of AppleConsumer. The classes should be rather private.
    public Consumer<Apple> createAppleConsumer() {
        return new AppleConsumer();
    }

    // ...and the same here
    public Consumer<Tomato> createTomatoConsumer() {
        return new TomatoConsumer();
    }
}

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

দয়া করে মনে রাখবেন যে প্রতিটি গ্রাহক সম্পূর্ণরূপে সম্পর্কিত না হলে স্বতন্ত্র (এখনও ব্যক্তিগত) শ্রেণি হতে পারে।

এই সমাধানটির খারাপ দিকটি হ'ল একটি উচ্চ শ্রেণীর জটিলতা (যদিও এটি একটি জাভা ফাইল হতে পারে) এবং গ্রাহক পদ্ধতিতে অ্যাক্সেস পেতে আপনার পরিবর্তে আরও একটি কল প্রয়োজন:

twoTypesConsumer.consume(apple)
twoTypesConsumer.consume(tomato)

তোমার আছে:

twoTypesConsumerFactory.createAppleConsumer().consume(apple);
twoTypesConsumerFactory.createTomatoConsumer().consume(tomato);

সংক্ষিপ্তসার হিসাবে আপনি 2 টি অভ্যন্তরীণ ক্লাস ব্যবহার করে একটি উচ্চ-স্তরের শ্রেণিতে 2 জেনেরিক গ্রাহককে সংজ্ঞায়িত করতে পারেন তবে কল করার ক্ষেত্রে আপনাকে প্রথমে যথাযথ প্রয়োগকারী ভোক্তার একটি রেফারেন্স পাওয়া দরকার কারণ এটি কেবল একটি ভোক্তা অবজেক্ট হতে পারে না।


1

কার্যক্ষম শৈলীতে এটি ইন্টারফেস বাস্তবায়ন না করে এটি করা সহজ এবং এটি সংকলন টাইম টাইপ চেকিংও করে।

সত্তা গ্রাস করার জন্য আমাদের কার্যকরী ইন্টারফেস

@FunctionalInterface
public interface Consumer<E> { 
     void consume(E e); 
}

আমাদের ব্যবস্থাপকটি যথাযথভাবে প্রক্রিয়াজাত করতে এবং সত্তাকে গ্রাস করতে

public class Manager {
    public <E> void process(Consumer<E> consumer, E entity) {
        consumer.consume(entity);
    }

    public void consume(Tomato t) {
        // Consume Tomato
    }

    public void consume(Apple a) {
        // Consume Apple
    }

    public void test() {
        process(this::consume, new Tomato());
        process(this::consume, new Apple());
    }
}

0

আরও ক্লাস ব্যবহার এড়ানোর জন্য আরেকটি বিকল্প। (উদাহরণস্বরূপ java8 + ব্যবহার করে)

// Mappable.java
public interface Mappable<M> {
    M mapTo(M mappableEntity);
}

// TwoMappables.java
public interface TwoMappables {
    default Mappable<A> mapableA() {
         return new MappableA();
    }

    default Mappable<B> mapableB() {
         return new MappableB();
    }

    class MappableA implements Mappable<A> {}
    class MappableB implements Mappable<B> {}
}

// Something.java
public class Something implements TwoMappables {
    // ... business logic ...
    mapableA().mapTo(A);
    mapableB().mapTo(B);
}

0

পুরানো প্রশ্নের উত্তরের জন্য দুঃখিত, তবে আমি এটি সত্যিই ভালবাসি! এই বিকল্পটি ব্যবহার করে দেখুন:

public class MegaConsumer implements Consumer<Object> {

  Map<Class, Consumer> consumersMap = new HashMap<>();
  Consumer<Object> baseConsumer = getConsumerFor(Object.class);

  public static void main(String[] args) {
    MegaConsumer megaConsumer = new MegaConsumer();
    
    //You can load your customed consumers
    megaConsumer.loadConsumerInMapFor(Tomato.class);
    megaConsumer.consumersMap.put(Apple.class, new Consumer<Apple>() {
        @Override
        public void consume(Apple e) {
            System.out.println("I eat an " + e.getClass().getSimpleName());
        }
    });
    
    //You can consume whatever
    megaConsumer.consume(new Tomato());
    megaConsumer.consume(new Apple());
    megaConsumer.consume("Other class");
  }

  @Override
  public void consume(Object e) {
    Consumer consumer = consumersMap.get(e.getClass());
    if(consumer == null) // No custom consumer found
      consumer = baseConsumer;// Consuming with the default Consumer<Object>
    consumer.consume(e);
  }

  private static <T> Consumer<T> getConsumerFor(Class<T> someClass){
    return t -> System.out.println(t.getClass().getSimpleName() + " consumed!");
  }

  private <T> Consumer<T> loadConsumerInMapFor(Class<T> someClass){
    return consumersMap.put(someClass, getConsumerFor(someClass));
  }
}

আমি মনে করি এটিই আপনি খুঁজছেন।

আপনি এই আউটপুট পেতে:

টমেটো সেবন!

আমি একটি আপেল খাই

স্ট্রিং গ্রাস!


প্রশ্নে: "তবে আমি সংকলন-টাইপ-পরীক্ষা-নিরীক্ষার সন্ধান করছি ..."
আয়ারাকোড

@ অ্যারাকোড ওপি চাইবে এমন কোনও বিকল্প নেই। প্রকারের মুছে ফেলা বিভিন্ন ধরণের ভেরিয়েবলের সাথে একই ইন্টারফেসটি দু'বার প্রয়োগ করতে অসম্পূর্ণ করে তোলে। আমি আপনাকে অন্যভাবে দেওয়ার চেষ্টা করি। অবশ্যই আপনি কোনও অ্যানজেক্ট গ্রাস করার জন্য পূর্বে গৃহীত প্রকারগুলি পরীক্ষা করতে পারেন।
Awes0meM4n
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.