Instanciation取决于类型,泛型,继承或两者?

时间:2017-12-01 13:47:28

标签: java android generics inheritance

我有一种方法,根据类型send作为此方法的参数来实例化特定类。这很有效,但我真的不喜欢这种if(c == class_name.class)方式来解决这个问题,而且我非常确定有更好的方法可以使用泛型和继承来实现这一点。我使用的方法看起来(可能是错误的......)对我来说很脏。

我遵循了几个关于泛型和继承的教程,但即使我认为我或多或少地分别理解这两个概念,我仍然要理解两者的混合......我认为这两个概念都可以解决我的问题混合。任何人都可以告诉我正确的跟踪,泛型,继承,两者或保持我的代码这样。有更好的方法吗?

这是我的实际方法:

private void addData(String csvFile, char separator, Class<?> c) {
        int lineNumber = 1;
        CSVReader reader;

        try {
            reader = new CSVReader(new InputStreamReader(getAssets().open(csvFile)), separator);
            String[] line;

            realm.beginTransaction();
            if(c == FlagDef.class) {
                while ((line = reader.readNext()) != null) {
                    FlagDef flagDef = new FlagDef(Long.parseLong(line[0]), line[1], line[2]);
                    Log.d(TAG, String.format("%s %s %s", line[0], line[1], line[2]));
                    realm.copyToRealm(flagDef);
                    lineNumber++;
                }
            }
            if(c == Picture.class) {
                while ((line = reader.readNext()) != null) {
                    Picture picture = new Picture(Long.parseLong(line[0]), line[1]);
                    Log.d(TAG, String.format("%s %s", line[0], line[1]));
                    realm.copyToRealm(picture);
                    lineNumber++;
                }
            }
            realm.commitTransaction();

            reader.close();
        } catch (FileNotFoundException ex) {
            Log.e(TAG, String.format("File %s not found : %s", csvFile, ex));
        } catch (IOException ex) {
            Log.e(TAG, String.format("Error parsing line number %s : %s", lineNumber, ex));
        }
    }

对此方法的调用:

addData(FILE_FLAGS,SEPARATOR,FlagDef.class);
addData(FILE_PICTURES,SEPARATOR,Picture.class);

可以找到copyToRealm方法的声明here

这适用于Android,我实际上是针对API 16。

为简洁起见,我仅指出两种类型PictureFlagDef,但我计划至少有10种不同的类型。

5 个答案:

答案 0 :(得分:2)

有类型擦除的概念,在运行时,泛型将被删除。你现在拥有的东西被认为是“干净的”,并且在这种情况下使用了很多。

我唯一能建议的是让你的task me(type: GradleBuild){ buildFile 'mybuild.gradle' //defaults to build.gradle so not needed dir 'someDir' tasks 'clean', 'build' } Picture可以实现标记界面,例如FlagDef(是的,名字非常糟糕),并将您的方法更改为:

CSVable

以便在编译时过滤您的类型(您只能传递实现<T extends CSV> void addData(Class<T> c) 的对象)

答案 1 :(得分:2)

Class<?> c参数更改为Function<String[], RealmModel>

private void addData(String csvFile, char separator, Function<String[], RealmModel> modelConstructor) {

而不是所有if块,你可以只有一个:

while ((line = reader.readNext()) != null) {
    RealmModel model = modelConstructor.apply(line);
    Log.d(TAG, String.join(" ", line));
    realm.copyToRealm(model);
    lineNumber++;
}

对addData方法的调用如下所示:

addData(FILE_FLAGS, SEPARATOR, line -> new FlagDef(Long.parseLong(line[0]), line[1], line[2]));
addData(FILE_PICTURES, SEPARATOR, line -> new Picture(Long.parseLong(line[0]), line[1]));

答案 2 :(得分:1)

您可以创建一个构建器类,该类将创建一个读取行的对象:

abstract class Builder<T> {
    abstract T build( String[] line);
}

有2个实现:

class FlagDefBuilder extends Builder<FlagDef > {

    @Override
    FlagDef build( String[] line )
    {
        FlagDef flagDef = new FlagDef(Long.parseLong(line[0]), line[1], line[2]);
        Log.d(TAG, String.format("%s %s %s", line[0], line[1], line[2]));
        return flagDef;
    } 

}

    class PictureBuilder extends Builder<FlagDef > {

        @Override
        FlagDef build( String[] line )
        {
            Picture picture = new Picture(Long.parseLong(line[0]), line[1]);
            Log.d(TAG, String.format("%s %s", line[0], line[1]));
            return picture;
        }       
    }

然后addData将是这样的:

    private void addData(String csvFile, char separator, Builder<?> b) {
        int lineNumber = 1;
        CSVReader reader;

        try {
            reader = new CSVReader(new InputStreamReader(getAssets().open(csvFile)), separator);
            String[] line;

            realm.beginTransaction();
                while ((line = reader.readNext()) != null) {
                    realm.copyToRealm(b.build(line));
                    lineNumber++;
                }
            }
            realm.commitTransaction();

            reader.close();
        } catch (FileNotFoundException ex) {
            Log.e(TAG, String.format("File %s not found : %s", csvFile, ex));
        } catch (IOException ex) {
            Log.e(TAG, String.format("Error parsing line number %s : %s", lineNumber, ex));
        }
    }

你可以这样调用它:

addData(FILE_FLAGS,SEPARATOR, new FlagDefBuilder());
addData(FILE_PICTURES,SEPARATOR, new PictureBuilder());

您可以在不修改addData的情况下添加第三种构建器类型。

答案 3 :(得分:1)

我对我的问题有几个答案。

我认为@VGR的解决方案很好。但问题是我的项目是针对Android API 16.所以在我的情况下,由@ Function was only introduced since API 24无法使用@VGR提出的功能解决方案。因此,这种解决方案可能缺乏未来发展的灵活性。无论如何,功能编程并不是我现在最难以判断的最佳技能。

StephaneM提出的解决方案是一个很好的解决方案,但是我得到它需要修改,不能一概而论。

@Eugene和@mickaël-b在评论中建议使用Strategy Design Pattern。这是我最终选择的解决方案。谢谢大家的回答和评论,这非常有帮助:)

enter image description here

请参阅下面的工作代码,如果我能以任何方式改进它,请随时发表评论。或者如果我犯了一些错误......我再次使用2个课程,因此这里有两个简洁的策略,但最后我会有十多个策略。

对于那些感兴趣的人,这段代码可用于从csv文件生成Realm数据库。

RealmDbGenerator dbGenerator = new RealmDbGenerator(this);    

    //flags
    dbGenerator.setGenerationStrategy(new FlagDefStrategy());
    dbGenerator.addData(FILE_FLAGS,SEPARATOR);

    //pictures
    dbGenerator.setGenerationStrategy(new PictureStrategy());
    dbGenerator.addData(FILE_PICTURES,SEPARATOR);

RealDbGenerator:

public class RealmDbGenerator {
    private static final String TAG = RealmDbGenerator.class.getSimpleName();

    public void setGenerationStrategy(GenerationStrategy generationStrategy) {
        this.generationStrategy = generationStrategy;
    }

    private GenerationStrategy generationStrategy;
    private Realm realm;
    private Context context;

    public RealmDbGenerator(Context context) {
        this.realm = Realm.getDefaultInstance();
        this.context = context;
    }

    public void addData(String csvFile, char separator) {
        CSVReader reader;

        int lineNumber = 1;

        try {
                reader = new CSVReader(new InputStreamReader(context.getAssets().open(csvFile)), separator);
                String[] line;

                realm.beginTransaction();
                while ((line = reader.readNext()) != null) {
                    generationStrategy.addData(line, realm);
                }
                realm.commitTransaction();

                reader.close();
        } catch (FileNotFoundException ex) {
            Log.e(TAG, String.format("File %s not found : %s", csvFile, ex));
        } catch (IOException ex) {
            Log.e(TAG, String.format("Error parsing line number %s : %s", lineNumber, ex));
        }
    }
}

策略界面:

public interface GenerationStrategy {
    void addData(String[] line, Realm realm);
}

FlagDefStrategy:

public class FlagDefStrategy implements GenerationStrategy {
    private static final String TAG = FlagDefStrategy.class.getSimpleName();

    @Override
    public void addData(String[] line, Realm realm) {
        FlagDef flagDef = new FlagDef(Long.parseLong(line[0]), line[1], line[2]);
        Log.d(TAG, String.format("%s %s %s", line[0], line[1], line[2]));
        realm.copyToRealm(flagDef);
    }
}

PictureStrategy:

public class PictureStrategy implements GenerationStrategy  {
    private static final String TAG = FlagDefStrategy.class.getSimpleName();

    @Override
    public void addData(String[] line, Realm realm) {
        Picture picture = new Picture(Long.parseLong(line[0]), line[1]);
        Log.d(TAG, String.format("%s %s", line[0], line[1]));
        realm.copyToRealm(picture);
    }
}

答案 4 :(得分:0)

我认为这是一个模块化问题,为什么不创建两种添加数据的方法并避免使用类类型参数?

private void addFlagData(String csvFile, char separator) {
...
}

private void addPictureData(String csvFile, char separator) {
...
}