-1

sorry if this is a duplicate question, but I'm getting kind of desperate to solve a problem for a school project (due date tomorrow). I'm very new to Java and this project involves storing data from certain objects to a local repository and also reading the data from the repo. The repository handler class is in one package and the objects are in another.

My problem is that I don't know how to make generic methods in the handler to be able to read and write any object that extends X.

For example, let's say I have Fruit.

Apple extends Fruit. Orange extends Fruit.

Both have their own unique attributes that I need to write/read to/from a file.

I wanted to have a method like ArrayList repo_reader(String filepath) That reads from a file and returns Apples and Oranges.

The only way that I know how to do this is having a field in the file stating which type of fruit it is, reading it and throwing it to a switch case like

switch (fruit_type){ case "Orange": Orange orange = new Orange(); orange.setOrangeSpecificAttribute("ble"); FruitBasket.add(orange); case "Apple": Apple apple = new Apple(); apple.setAppleSpecificAttribute("bla"); FruitBasket.add(apple);

But then the method wouldn't be generic. Everytime that someone creates a new Fruit class, they would have to also change the repo_handler methods accordingly. The same would also happen with the writing method.

I could have the Fruit classes all implement their own method to write and read, but I don't want that. I want repo_handler class to deal with all the file reading and writing.

Again, sorry if it's a stupid question, and thanks for your guys' attention!

Btw, it's a CSV file, forgot to mention.

1 Answers1

0

You may want to have different readers/factories dependending on the contents of some String. That is, the String acts as identifier for the reader/factory to use.

You can use a map to map identifiers (keys) to readers/factories (values). As the latter must be registered, I'd use the service loader mechanism as described in ServiceLoader, and iterate over all services to register them in this map.

Having this, you can look up the needed reader/factory in the map and use it to read/create new Fruit instances.

Step #1: Service Provider Interface

package com.mycompany.fruit;

public interface FruitFactory {
    String getIdentifier();
    Fruit createFruit(String[] attributes); // change params to your needs
}

Step #2: Service implementation

package com.mycompany.fruit;

public class AppleFactory implements FruitFactory {
    public String getIdentifier() { return "Apple"; }
    public Fruit createFruit(String[] attributes) {
        Apple apple = new Apple();
        apple.setCommonAttribute(attributes[1]);
        apple.setSpecificAttribute(attributes[2]);
        // ...
        return apple;
    }
}

Step #3: Register factories

Put a file com.mycompany.fruit.FruitFactory in META-INF/services. Put the fully qualified class name of each implementation of FruitFactory on separate lines in this file. For example:

com.mycompany.fruit.AppleFactory
com.mycompany.fruit.OrangeFactory
...

Step #4: Load the services and use them

public class MyFruitReader {
    Map<String, FruitFactory> factories;

    public MyFruitReader(...) {
        ServiceLoader<FruitFactory> loader = new ServiceLoader(FruitFactory.class);
        for (FruitFactory factory : loader) {
            factories.put(factory.getIdentifier(), factory);
        }

    // usage 
    private Fruit getFruit(String[] row) {
        String fruitType = row[0];
        FruitFactory factory = factories.get(fruitType);
        if (factory == null) {
            return null;
        }
        return factory.createFruit(row);
    }

    // read the CSV file and call getFruit()
    // ...

Keep in mind, that this are just sketches to provide a rough overview of this topic. You'll have to adapt it to your needs, add exception handling, fix errors, and so on.

Mihe
  • 2,270
  • 2
  • 4
  • 14