0

It's really hard to summarize the problem but I will try my best and hope you get it, I have a enum that implements a functional interface (Execution) where the only method in Execution is execute that accept two parameters

1: AbstractRepository: spring-jpa interface

2: String

the thing is with each sub enum I need to implement execute method but rather than making it accept AbstractRepository I want to make it accept interface that extends AbstractRepository.

this is the enum with the functional interface.

package com.deepTech.Familytree.enums;

import com.deepTech.Familytree.domain.ImageBox;
import com.deepTech.Familytree.domain.Person;
import com.deepTech.Familytree.exception.PersonException;
import com.deepTech.Familytree.repository.AbstractRepository;

import java.util.Optional;

import static com.deepTech.Familytree.config.AuthDataConfig.getUser;
import static com.deepTech.Familytree.exception.PersonException.ExceptionType.PERSON_NOT_FOUND;


public enum FileManagementEnumExecution implements Execution {


    UPLOAD_PERSON_FILE() {
        @Override
               // rather than AbstractRepository I want to make it accepts a repository that extends an AbstractRepository
        public void execute(AbstractRepository repository, String filename) {
            Optional<Person> person1 = (Optional<Person>) repository
                    .finByTEm(getUser().getEmail());

            person1.map(person -> person.copyByImage(new ImageBox(filename)));


            repository.save(person1.orElseThrow(() -> new PersonException(PERSON_NOT_FOUND)));
        }
    },

    DELETE_PERSON_FILE() {
        @Override
        public void execute(AbstractRepository repository, String filename) {
            Optional<Person> person1 = (Optional<Person>) repository
                    .finByTEm(getUser().getEmail());

            repository.save(person1
                    .map(Person::withoutImage)
                    .orElseThrow(() -> new PersonException(PERSON_NOT_FOUND)));

        }
    },
    UPLOAD_VIP_PERSON_FILE() {
        @Override
        public void execute(AbstractRepository repository, String filename) {

        }
    },

    DELETE_VIP_PERSON_FILE() {
        @Override
        public void execute(AbstractRepository repository, String filename) {


        }
    },
    UPLOAD_NEWS_FILE() {
        @Override
        public void execute(AbstractRepository repository, String filename) {

        }
    },

    DELETE_NEWS_FILE() {
        @Override
        public void execute(AbstractRepository repository, String filename) {


        }
    };

}

@FunctionalInterface
interface Execution {
    void execute(AbstractRepository repository, String filename);
}

@Service
@RequiredArgsConstructor
public class PersonServiceImpl implements PersonInterface {
...
 /* somecode */
...
        @Override
    public UploadFileResponse uploadPersonFile(Long id, MultipartFile file) {
        return fileManagementService.uploadFile(file, id, UPLOAD_PERSON_FILE, personRepository);

    }
...
 /* somecode */
...
}

and here FileManagementService class

@Service
@RequiredArgsConstructor
public class FileManagementService {
...
 /* somecode */
...
    public UploadFileResponse uploadFile(MultipartFile file, Long id, SsssEnum ssssEnum, AbstractRepository neo4jRepository) {
        String fileName = fileStorageService.storeFile(file);

        String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
                .path("/downloadFile/")
                .path(fileName)
                .toUriString();

        FileManagementEnumExecution.execute(neo4jRepository, fileName);

        return new UploadFileResponse(fileName, fileDownloadUri,
                file.getContentType(), file.getSize());
    }
...
 /* somecode */
...
}

can anybody help please?

suliman
  • 334
  • 6
  • 17
  • I casted each repository to the intended one inside each sub-enum, anyway the question still remain! is there another solution ? – suliman Oct 06 '19 at 18:46

1 Answers1

1

If the class implements some interface, it can't specify more concrete types of method parameters than the interface, because class instances can be safely upcasted to interface type and methods can be called with arguments of the type which specified in the interface. Check out the Liskov substitution principle for more information.

To let implementations specify types of some parameters and methods, you should add the corresponding type parameters to your interface. Here is how you can do it in your particular case:

@FunctionalInterface
public interface Execution<T extends AbstractRepository> {
    void execute(T repository, String fileName);
}

When you implement this interface, you can specify the type parameter either with actual type or with another type parameter. In your case, you should use actual type if all FileManagementExecutions accepts the same type of repository:

public enum FileManagementExecution implements Execution<ConcreteReprisitory> {
    UPLOAD_PERSON_FILE() {
        @Override
        public void execute(ConcreteReprisitory repository, String fileName) {
            //TODO
        }
    },
    DELETE_PERSON_FILE() {
        @Override
        public void execute(ConcreteReprisitory repository, String fileName) {
            //TODO
        }
    }
    // other enum constants
}

If FileManagementExecutions accepts different types of a repository, you should add a type parameter to FileManagementExecution as well. Unfortunately, Java doesn't support generic enums, so in this case, you have to use the regular class:

public abstract class FileManagementExecution<T extends AbstractRepository> implements Execution<T> {
    private FileManagementExecution() {}

    public static final FileManagementExecution<ConcreteReprisitory1> UPLOAD_PERSON_FILE =
            new FileManagementExecution<ConcreteReprisitory1>() {
                @Override
                public void execute(ConcreteReprisitory1 repository, String fileName) {
                    //TODO
                }
            };
    public static final FileManagementExecution<ConcreteReprisitory2> DELETE_PERSON_FILE =
            new FileManagementExecution<ConcreteReprisitory2>() {
                @Override
                public void execute(ConcreteReprisitory2 repository, String fileName) {
                    //TODO
                }
            };
    // other constants
}

To make code a little bit cleaner, you can replace abstract class with interface1:

public interface FileManagementExecution<T extends AbstractRepository> extends Execution<T> {
    FileManagementExecution<ConcreteReprisitory1> UPLOAD_PERSON_FILE = 
            ((repository, fileName) -> TODO());
    FileManagementExecution<ConcreteReprisitory2> DELETE_PERSON_FILE = 
            ((repository, fileName) -> TODO());
    // other constants
}

1 If you do it, you won't be able to forbid implementing this interface.

IlyaMuravjov
  • 2,352
  • 1
  • 9
  • 27