2014-05-21 2 views
1

나는 이런 식으로 수업을 많이 하바 :개념 증명 : 리플렉션을 사용하여 일반 비교기 메서드를 만드는 방법은 무엇입니까?

Class Person { 
    protected String name; 
    protected String surname; 
    ...getters and setters... 
} 

내가 이름이나 성으로 사람의 컬렉션을 주문하고 싶습니다.

Collections.sort(listofpersons, new Comparator<Person>(){ 
    @Override 
    public int compare(Person p1, Person p2) { return p1.getName().compareTo(p2.getName()); }  
}) 

또는

Collections.sort(listofpersons, new Comparator<Person>(){ 
    @Override 
    public int compare(Person p1, Person p2) { return p1.getSurname().compareTo(p2.getSurname()); }  
}) 

내가 좋아하는, 일반적인 비교기를 구현하기 위해 노력하고있어 : 사실은 내가 간단하게이 일을하고있어

MyUtils.sort(listofpersons,"getName"); 
MyUtils.sort(listofpersons,"getSurame"); 

난의 이해하려고 노력 중이 야 제네릭과 리플렉션을 통해이를 수행 할 수는 있지만 막혔습니다. 나는이 일을 해요 :

public static <T> void sortCollection(Collection<T> list, final String methodName) { 
    Comparator<T> comparator = new Comparator<T>() { 
    @Override 
    public int compare(T o1, T o2) { 
     try { 
     String a=o1.getClass().getMethod(methodName).invoke(o1).toString(); 
     String b=o2.getClass().getMethod(methodName).invoke(o2).toString(); 
     return a.compareTo(b); 
     } catch (Exception ex) { 
     ...log somewhere... 
     return 0; 
     } 
    } 
    }; 
    Collections.sort(list, comparator); 
} 

는 캐치 예외와 지금 캐스팅 문자열을 잊지는 점은, Collections.sort (컬렉션, 비교기가) 존재하지 않는다는 것입니다,하지만 난을 만드는 모르겠어요 Comparator ... 내 아이디어가 어떤 의미인지 아닌지 (이유)와 올바른 구현 방법을 알고 있는지 궁금합니다.

감사합니다.

답변

1

문제는 sortCollection 방법의 서명에 있습니다

는에 메소드 서명을 변경

. list 매개 변수의 유형을 Collection<T>에서 List<T>으로 변경해야합니다.즉, 당신의 방법의 헤더는 있어야한다 :

public static <T> void sortCollection(List<T> list, final String methodName) 

의 그 Collections.sort()는 첫 번째 매개 변수는 List (참조의 javadoc)

편집 할 것으로 예상하기 때문에 : 구현에 관한

, 거기에 몇 가지를 일반/반사 트릭을 사용할 수 있습니다. 여기에 가능한 대안이 있습니다. 여기

@SuppressWarnings({ "unchecked", "rawtypes" }) 
public static<T> Comparator<T> 
    newMethodComparator(Class<T> cls, String methodName) throws Exception {  
    Method method = cls.getMethod(methodName); 
    if (method.getParameterTypes().length != 0) 
    throw new Exception("Method " + method + " takes parameters"); 

    Class<?> returnType = method.getReturnType(); 
    if (!Comparable.class.isAssignableFrom(returnType)) 
    throw new Exception("The return type " + returnType + " is not Comparable"); 

    return newMethodComparator(method, (Class<? extends Comparable>) returnType);  
} 

private static<T,R extends Comparable<R>> Comparator<T> newMethodComparator(
    final Method method, final Class<R> returnType) throws Exception {  
    return new Comparator<T>() { 
    @Override 
    public int compare(T o1, T o2) { 
     try { 
     R a = invoke(method, o1); 
     R b = invoke(method, o2); 
     return a.compareTo(b); 
     } catch (Exception e) { 
     throw new RuntimeException(e); 
     } 
    } 

    private R invoke(Method method, T o) throws Exception { 
     return returnType.cast(method.invoke(o)); 
    } 
    }; 
} 

당신이 그것을 사용하는 것이 방법은 다음과 같습니다

List<Person> ps = new ArrayList<>(Arrays.asList(
    new Person("A", "D"), new Person("B", "C"))); 
    ... 

    Comparator<Person> byNameComparator = 
    newMethodComparator(Person.class, "getName"); 
    Collections.sort(ps, byNameComparator); 

이론적 근거 :

(A)이 IMPL. Comparable 개체를 반환하는 모든 메서드에서 작동합니다. 당신의 구현은 string-comparison을 사용하고 있습니다. 문자열 비교는 메소드가 int를 돌려 주면 멋지게 작동하지 않을 것입니다.

(b)이 impl. 메소드의 리턴 유형과 Comparator이 작성된 지점에서 0 인수를 취합니다 (,). 정렬이 시작되었습니다. 따라서 Collections.sort()이 예외를 throw 할 확률이 적습니다.

(c) @SuppressWarnings({ "unchecked", "rawtypes" })은 실제 위험을 초래하지 않습니다. 메서드의 반환 유형이 Comparable이면 다운 캐스트가 성공합니다. 반환 유형이 Comparable이 아니면 Comparable.class.isAssignableFrom(returnType))이 false를 반환하면 메서드는 명시 적 예외를 throw하고 다운 캐스트에 도달하지 않습니다.

+0

설명과 샘플을 보내 주셔서 감사합니다. –

0

질문의 시작 부분에서 보여준 것과 유사한 여러 비교기의 구체적인 구현을 만드는 것이 가장 좋다고 말할 수 있습니다.

public class PersonNameComparator implements Comparator<Person> 
{ 
    public int compareTo(Person o1, Person o2) 
    { 
     return o1.getName().compareTo(o2.getName()); 
    } 
} 

public class PersonSurnameComparator implements Comparator<Person> 
{ 
    public int compareTo(Person o1, Person o2) 
    { 
     return o1.getSurname().compareTo(o2.getSurname()); 
    } 
} 

그럼 그냥 방법을 분류 표준 라이브러리 컬렉션 전화

당신이 IDE를 가진 끝낼 것 중산 판 코드를 많이이기 때문에 그것은 아마 코드 냄새의 비트입니다
Collections.sort(listofpersons, new PersonNameComparator()); 
Collections.sort(listofpersons, new PersonSurameComparator()); 

생성하지만, 적어도 단위 테스트를 작성하는 것이 쉽고, 리플렉션을 사용하는 것보다 (약간만 그렇다면) 더 성능이 좋은 장점이 있습니다.

짧은 접근 방식은 Person 클래스에 새 메서드를 추가 할 때마다 새로운 비교자를 구현해야하기 때문에 여기에 많은 부 풀림이 있습니다.

0

콜렉션을 정렬 할 수 없으며 리스트 만 정렬 할 수 있습니다. 이는 모든 컬렉션 (예 : Set)이 요소 "주문"개념을 지원하지 않기 때문입니다.

public static <T> void sort(List<T> list, final String methodName) { 
관련 문제