메소드 리턴 유형을 일반으로 만들려면 어떻게해야합니까?


588

이 예를 고려하십시오 (OOP 서적에 일반적 임).

나는 많은 친구들을 가질 수 있는 Animal수업이 Animal있습니다.
그리고 서브 클래스는 좋아 Dog, Duck, Mouse등 같은 특정 동작을 추가하는 bark(), quack()

Animal수업 은 다음과 같습니다 .

public class Animal {
    private Map<String,Animal> friends = new HashMap<>();

    public void addFriend(String name, Animal animal){
        friends.put(name,animal);
    }

    public Animal callFriend(String name){
        return friends.get(name);
    }
}

그리고 여기에는 많은 유형 캐스팅이있는 코드 스 니펫이 있습니다.

Mouse jerry = new Mouse();
jerry.addFriend("spike", new Dog());
jerry.addFriend("quacker", new Duck());

((Dog) jerry.callFriend("spike")).bark();
((Duck) jerry.callFriend("quacker")).quack();

타입 캐스팅을 없애기 위해 리턴 타입에 제네릭을 사용할 수있는 방법이 있습니까?

jerry.callFriend("spike").bark();
jerry.callFriend("quacker").quack();

다음은 사용되지 않은 매개 변수로 메소드에 전달 된 리턴 유형의 일부 초기 코드입니다.

public<T extends Animal> T callFriend(String name, T unusedTypeObj){
    return (T)friends.get(name);        
}

를 사용하여 추가 매개 변수없이 런타임에 반환 유형을 파악하는 방법이 instanceof있습니까? 또는 적어도 더미 인스턴스 대신 유형의 클래스를 전달하여.
제네릭이 컴파일 타임 유형 검사를위한 것임을 이해하지만 이에 대한 해결 방법이 있습니까?

답변:


903

callFriend이 방법으로 정의 할 수 있습니다 .

public <T extends Animal> T callFriend(String name, Class<T> type) {
    return type.cast(friends.get(name));
}

그런 다음 다음과 같이 호출하십시오.

jerry.callFriend("spike", Dog.class).bark();
jerry.callFriend("quacker", Duck.class).quack();

이 코드는 컴파일러 경고를 생성하지 않는 이점이 있습니다. 물론 이것은 실제로 제네시스 이전부터 업데이트 된 캐스팅 버전이며 추가적인 안전성을 추가하지 않습니다.


29
...하지만 callFriend () 호출의 매개 변수 사이에는 여전히 컴파일 시간 유형 검사가 없습니다.
David Schmitt

2
이것이 지금까지 가장 좋은 대답이지만 addFriend를 같은 방식으로 변경해야합니다. 두 곳에서 클래스 리터럴이 필요하기 때문에 버그를 작성하기가 더 어려워집니다.
Craig P. Motlin

@Jaider, 정확히 동일하지는 않지만 작동합니다. // Animal Class public T CallFriend <T> (string name) 여기서 T : Animal {return friends [name] as T; } // 클래스 호출 jerry.CallFriend <Dog> ( "spike"). Bark (); jerry.CallFriend <Duck> ( "quacker"). Quack ();
Nestor Ledon

124

아니요. 컴파일러는 어떤 유형 jerry.callFriend("spike")이 반환 되는지 알 수 없습니다 . 또한 구현시 추가 유형 안전없이 메소드에서 캐스트를 숨 깁니다. 이걸 고려하세요:

jerry.addFriend("quaker", new Duck());
jerry.callFriend("quaker", /* unused */ new Dog()); // dies with illegal cast

이 특정한 경우 추상 talk()메소드를 작성 하고 서브 클래스에서 적절하게 재정의하면 훨씬 나은 결과를 얻을 수 있습니다.

Mouse jerry = new Mouse();
jerry.addFriend("spike", new Dog());
jerry.addFriend("quacker", new Duck());

jerry.callFriend("spike").talk();
jerry.callFriend("quacker").talk();

10
mmyers 방법이 작동 할 수 있지만이 방법은 OO 프로그래밍이 더 좋으며 향후 문제를 덜어 줄 것이라고 생각합니다.
James McMahon

1
이것은 동일한 결과를 얻는 올바른 방법입니다. 목표는 추악한 유형 검사 및 캐스팅을 수행하기 위해 코드를 직접 작성하지 않고 런타임에 파생 클래스 특정 동작을 얻는 것입니다. @laz가 제안한 방법은 작동하지만 유형 안전을 창 밖으로 내 보냅니다. 이 방법에는 코드 줄이 더 적게 필요합니다 (메서드 구현이 늦게 실행되어 런타임에 조회되기 때문에). 그러나 여전히 Animal의 각 하위 클래스에 대해 고유 한 동작을 쉽게 정의 할 수 있습니다.
dcow

그러나 원래의 질문은 유형 안전에 대해 묻지 않습니다. 내가 읽은 방법은 캐스팅을 피하기 위해 제네릭을 활용할 수있는 방법이 있는지 알고 싶어합니다.
laz

2
@laz : 네, 제기 된 원래의 질문은 타입 안전에 관한 것이 아닙니다. 클래스 캐스트 실패를 제거하여 안전한 구현 방법이 있다는 사실은 변하지 않습니다. weblogs.asp.net/alex_papadimoulis/archive/2005/05/25/…
David Schmitt

3
나는 그것에 동의하지 않지만, 우리는 Java와 모든 디자인 결정 / 거품을 다루고 있습니다. 난 그냥 아닌 xyproblem (로, 자바 제네릭에서 가능한 것을 배우려고 노력으로이 질문을 참조 meta.stackexchange.com/questions/66377/what-is-the-xy-problem 요구를 재 설계 할 것을). 다른 패턴이나 접근법과 마찬가지로, 내가 제공 한 코드가 적절한 시간과 완전히 다른 무언가 (이 답변에서 제안한 것과 같은)가 필요할 때가 있습니다.
laz

114

다음과 같이 구현할 수 있습니다.

@SuppressWarnings("unchecked")
public <T extends Animal> T callFriend(String name) {
    return (T)friends.get(name);
}

(예, 이것은 올바른 코드입니다. Java 제네릭 : 반환 유형으로 만 정의 된 제네릭 형식 참조 )

반환 유형은 호출자로부터 유추됩니다. 그러나 @SuppressWarnings주석 : 이 코드는 형식 안전하지 않다는 것을 나타 냅니다. 직접 확인해야하거나 ClassCastExceptions런타임에 얻을 수 있습니다.

불행히도, 당신이 그것을 사용하는 방법 (반환 값을 임시 변수에 할당하지 않고), 컴파일러를 행복하게 만드는 유일한 방법은 다음과 같이 호출하는 것입니다.

jerry.<Dog>callFriend("spike").bark();

이것은 캐스팅보다 조금 더 좋지만 David Schmitt가 말한 것처럼 Animal클래스에 추상적 인 talk()방법을 제공하는 것이 좋습니다 .


메소드 체인은 실제로 의도 된 것이 아닙니다. 하위 유형 변수에 값을 할당하고 사용하는 것을 신경 쓰지 않습니다. 솔루션 주셔서 감사합니다.
Sathish

이것은 메소드 콜 체인을 할 때 완벽하게 작동합니다!
Hartmut P.

나는이 문법을 정말 좋아한다. C #에서는 jerry.CallFriend<Dog>(...더 좋아 보인다고 생각합니다.
andho

JRE 자신의 java.util.Collections.emptyList()함수가 이와 같이 정확하게 구현되어 있고 javadoc은 타입 안전하다고 광고합니다.
Ti Strga

31

이 질문은 Effective Java의 " 항목 안전 이종 컨테이너 고려" 항목 29 와 매우 유사합니다 . Laz의 답변은 Bloch의 솔루션에 가장 가깝습니다. 그러나 put과 get은 모두 안전을 위해 클래스 리터럴을 사용해야합니다. 서명은 다음과 같습니다.

public <T extends Animal> void addFriend(String name, Class<T> type, T animal);
public <T extends Animal> T callFriend(String name, Class<T> type);

두 방법 모두에서 매개 변수가 제정신인지 확인해야합니다. 자세한 정보는 효과적인 Java 및 클래스 javadoc을 참조하십시오.


17

또한 메소드에게 주어진 유형의 값을 이런 식으로 반환하도록 요청할 수 있습니다

<T> T methodName(Class<T> var);

더 많은 예제 여기에 오라클 자바 문서에서


17

더 간단한 버전은 다음과 같습니다.

public <T> T callFriend(String name) {
    return (T) friends.get(name); //Casting to T not needed in this case but its a good practice to do
}

완전 작업 코드 :

    public class Test {
        public static class Animal {
            private Map<String,Animal> friends = new HashMap<>();

            public void addFriend(String name, Animal animal){
                friends.put(name,animal);
            }

            public <T> T callFriend(String name){
                return (T) friends.get(name);
            }
        }

        public static class Dog extends Animal {

            public void bark() {
                System.out.println("i am dog");
            }
        }

        public static class Duck extends Animal {

            public void quack() {
                System.out.println("i am duck");
            }
        }

        public static void main(String [] args) {
            Animal animals = new Animal();
            animals.addFriend("dog", new Dog());
            animals.addFriend("duck", new Duck());

            Dog dog = animals.callFriend("dog");
            dog.bark();

            Duck duck = animals.callFriend("duck");
            duck.quack();

        }
    }

1
무엇을합니까 Casting to T not needed in this case but it's a good practice to do? 런타임 중에 올바르게 처리하면 "좋은 방법"이란 무엇입니까?
Farid

메소드 선언에 대한 리턴 타입 선언 <T>이 충분해야하기 때문에 명시 적 캐스팅 (T)은 필요하지 않습니다.
webjockey

9

수업을 통과해도 괜찮을 것이라고 말했듯이 다음과 같이 작성할 수 있습니다.

public <T extends Animal> T callFriend(String name, Class<T> clazz) {
   return (T) friends.get(name);
}

그런 다음 다음과 같이 사용하십시오.

jerry.callFriend("spike", Dog.class).bark();
jerry.callFriend("quacker", Duck.class).quack();

완벽하지는 않지만 Java 제네릭을 사용하는 경우와 거의 같습니다. Super Type Tokens를 사용하여 Typesafe Hterogenous Containers (THC) 를 구현하는 방법이 있지만 자체 문제가 다시 발생합니다.


미안하지만 이것은 laz와 똑같은 대답이므로, 당신은 그를 복사하거나 그가 당신을 복사하고 있습니다.
James McMahon

이것이 Type을 전달하는 깔끔한 방법입니다. 그러나 Schmitt가 말한 것처럼 여전히 안전하지 않습니다. 나는 여전히 다른 수업을 통과 할 수 있었고 타입 캐스팅은 폭탄을 터뜨릴 것입니다. mmyers 리턴 유형을 설정하는 두 번째 응답이 더 나은 것 같습니다
Sathish

4
니모, 당신이 게시 시간을 확인하면 우리는 거의 정확히 같은 순간에 게시했습니다. 또한 그들은 정확히 동일하지 않으며 두 줄만 있습니다.
Fabian Steeg

@ Fabian 나는 비슷한 대답을 게시했지만 Bloch의 슬라이드와 Effective Java에 게시 된 내용에는 중요한 차이점이 있습니다. 그는 TypeRef <T> 대신 Class <T>를 사용합니다. 그러나 이것은 여전히 ​​좋은 대답입니다.
Craig P. Motlin

8

수퍼 유형 토큰과 동일한 아이디어를 기반으로 문자열 대신 사용할 유형이 지정된 ID를 만들 수 있습니다.

public abstract class TypedID<T extends Animal> {
  public final Type type;
  public final String id;

  protected TypedID(String id) {
    this.id = id;
    Type superclass = getClass().getGenericSuperclass();
    if (superclass instanceof Class) {
      throw new RuntimeException("Missing type parameter.");
    }
    this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
  }
}

그러나 각 문자열마다 새로운 id 객체를 생성하고 유지해야하거나 올바른 유형 정보로 재구성해야하기 때문에 이것이 목적을 무효화 할 수 있다고 생각합니다.

Mouse jerry = new Mouse();
TypedID<Dog> spike = new TypedID<Dog>("spike") {};
TypedID<Duck> quacker = new TypedID<Duck>("quacker") {};

jerry.addFriend(spike, new Dog());
jerry.addFriend(quacker, new Duck());

그러나 이제 캐스트없이 원래 원하는 방식으로 클래스를 사용할 수 있습니다.

jerry.callFriend(spike).bark();
jerry.callFriend(quacker).quack();

이것은 ID 내에 type 매개 변수를 숨기고 있지만 나중에 원하는 경우 식별자에서 유형을 검색 할 수 있음을 의미합니다.

동일한 ID의 두 인스턴스를 비교하려면 TypedID의 비교 및 ​​해시 메소드도 구현해야합니다.


8

"Instanceof를 사용하여 추가 매개 변수없이 런타임에 리턴 유형을 파악할 수있는 방법이 있습니까?"

대안 솔루션으로 이와 같은 방문자 패턴을 활용할 수 있습니다. Animal을 abstract로 만들고 그것을 Visitable을 구현하게하십시오 :

abstract public class Animal implements Visitable {
  private Map<String,Animal> friends = new HashMap<String,Animal>();

  public void addFriend(String name, Animal animal){
      friends.put(name,animal);
  }

  public Animal callFriend(String name){
      return friends.get(name);
  }
}

방문 가능이란 동물 구현이 방문자를 기꺼이 수락한다는 의미입니다.

public interface Visitable {
    void accept(Visitor v);
}

방문자 구현은 동물의 모든 하위 클래스를 방문 할 수 있습니다.

public interface Visitor {
    void visit(Dog d);
    void visit(Duck d);
    void visit(Mouse m);
}

예를 들어 Dog 구현은 다음과 같습니다.

public class Dog extends Animal {
    public void bark() {}

    @Override
    public void accept(Visitor v) { v.visit(this); }
}

여기서의 요령은 Dog가 어떤 유형인지 알기 때문에 "this"를 매개 변수로 전달하여 방문자 v의 관련 과부하 된 방문 방법을 트리거 할 수 있다는 것입니다. 다른 서브 클래스는 accept ()를 정확히 같은 방식으로 구현합니다.

서브 클래스 특정 메소드를 호출하려는 클래스는 다음과 같이 방문자 인터페이스를 구현해야합니다.

public class Example implements Visitor {

    public void main() {
        Mouse jerry = new Mouse();
        jerry.addFriend("spike", new Dog());
        jerry.addFriend("quacker", new Duck());

        // Used to be: ((Dog) jerry.callFriend("spike")).bark();
        jerry.callFriend("spike").accept(this);

        // Used to be: ((Duck) jerry.callFriend("quacker")).quack();
        jerry.callFriend("quacker").accept(this);
    }

    // This would fire on callFriend("spike").accept(this)
    @Override
    public void visit(Dog d) { d.bark(); }

    // This would fire on callFriend("quacker").accept(this)
    @Override
    public void visit(Duck d) { d.quack(); }

    @Override
    public void visit(Mouse m) { m.squeak(); }
}

나는 그것이 당신이 거래했던 것보다 훨씬 더 많은 인터페이스와 메소드라는 것을 알고 있지만, 정확히 제로 인스턴스 검사와 제로 타입 캐스트로 모든 특정 하위 유형을 처리하는 표준 방법입니다. 그리고 그것은 모두 표준 언어에 구애받지 않는 방식으로 이루어 지므로 Java뿐만 아니라 모든 OO 언어가 동일하게 작동해야합니다.


6

불가능합니다. 문자열 키만 주어지면 맵의 Animal 하위 클래스를 어떻게 알 수 있습니까?

이것이 가능한 유일한 방법은 각 Animal이 한 가지 유형의 친구 만 허용 한 다음 (Animal 클래스의 매개 변수 일 수 있음) callFriend () 메소드의 유형 매개 변수를 얻은 경우입니다. 그러나 실제로 상속 지점이 누락 된 것처럼 보입니다. 슈퍼 클래스 메소드를 독점적으로 사용할 때 서브 클래스를 균일하게 처리 할 수 ​​있습니다.


5

개념 증명, 지원 클래스 및 런타임에 클래스에서 수퍼 유형 토큰을 검색하는 방법을 보여주는 테스트 클래스가 포함 된 기사를 작성했습니다. 간단히 말해, 호출자가 전달한 실제 일반 매개 변수에 따라 대체 구현에 위임 할 수 있습니다. 예:

  • TimeSeries<Double> 사용하는 개인 내부 클래스에 위임 double[]
  • TimeSeries<OHLC> 사용하는 개인 내부 클래스에 위임 ArrayList<OHLC>

참조 : TypeToken을 사용하여 일반 매개 변수 검색

감사

Richard Gomes- 블로그


사실, 당신의 통찰력을 공유해 주셔서 감사합니다. 당신의 기사는 정말로 모든 것을 설명합니다!
Yann-Gaël Guéhéneuc

3

여기에는 많은 훌륭한 답변이 있지만 이것이 단일 요소에 대한 행동으로 인해 사용자 설정에 따라 다른 응용 프로그램 상태가 될 수있는 Appium 테스트에 대한 접근 방식입니다. OP의 예제 규칙을 따르지 않지만 누군가에게 도움이되기를 바랍니다.

public <T extends MobilePage> T tapSignInButton(Class<T> type) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    //signInButton.click();
    return type.getConstructor(AppiumDriver.class).newInstance(appiumDriver);
}
  • MobilePage는 유형이 확장되는 수퍼 클래스로, 모든 하위 (duh)를 사용할 수 있음을 의미합니다.
  • type.getConstructor (Param.class 등)를 사용하면 형식의 생성자와 상호 작용할 수 있습니다. 이 생성자는 모든 예상 클래스간에 동일해야합니다.
  • newInstance는 새 객체 생성자에 전달할 선언 된 변수를 사용합니다.

오류를 발생시키지 않으려면 다음과 같이 오류를 잡을 수 있습니다.

public <T extends MobilePage> T tapSignInButton(Class<T> type) {
    // signInButton.click();
    T returnValue = null;
    try {
       returnValue = type.getConstructor(AppiumDriver.class).newInstance(appiumDriver);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return returnValue;
}

내가 이해하기에, 이것은 제네릭을 사용하는 가장 좋고 가장 우아한 방법입니다.
cbaldan

2

실제로는 컴파일러가 callFriend ()가 Dog 또는 Duck이 아닌 Animal을 반환한다는 것을 알고 있기 때문에 실제로는 아닙니다.

서브 클래스에 의해 나무 껍질 또는 as으로 구현 될 추상 makeNoise () 메소드를 Animal에 추가 할 수 없습니까?


1
동물들이 추상화 할 수있는 일반적인 행동에 속하지 않는 여러 방법을 가지고 있다면 어떨까요? 인스턴스가 아닌 Type을 전달해도 괜찮은 다른 작업으로 서브 클래스 간 통신을 위해서는 이것이 필요합니다.
Sathish

2
당신은 정말로 당신 자신의 질문에 답했습니다 – 만약 동물이 독특한 행동을한다면, 당신은 그 특정 동물에게 던져 져야합니다. 동물에 다른 동물과 함께 그룹화 할 수있는 동작이있는 경우 기본 클래스에서 추상 또는 가상 방법을 정의하여 사용할 수 있습니다.
Matt Jordan

2

여기서 찾고있는 것은 추상화입니다. 인터페이스에 대한 코드가 많으므로 캐스팅이 적어야합니다.

아래 예제는 C #에 있지만 개념은 동일합니다.

using System;
using System.Collections.Generic;
using System.Reflection;

namespace GenericsTest
{
class MainClass
{
    public static void Main (string[] args)
    {
        _HasFriends jerry = new Mouse();
        jerry.AddFriend("spike", new Dog());
        jerry.AddFriend("quacker", new Duck());

        jerry.CallFriend<_Animal>("spike").Speak();
        jerry.CallFriend<_Animal>("quacker").Speak();
    }
}

interface _HasFriends
{
    void AddFriend(string name, _Animal animal);

    T CallFriend<T>(string name) where T : _Animal;
}

interface _Animal
{
    void Speak();
}

abstract class AnimalBase : _Animal, _HasFriends
{
    private Dictionary<string, _Animal> friends = new Dictionary<string, _Animal>();


    public abstract void Speak();

    public void AddFriend(string name, _Animal animal)
    {
        friends.Add(name, animal);
    }   

    public T CallFriend<T>(string name) where T : _Animal
    {
        return (T) friends[name];
    }
}

class Mouse : AnimalBase
{
    public override void Speak() { Squeek(); }

    private void Squeek()
    {
        Console.WriteLine ("Squeek! Squeek!");
    }
}

class Dog : AnimalBase
{
    public override void Speak() { Bark(); }

    private void Bark()
    {
        Console.WriteLine ("Woof!");
    }
}

class Duck : AnimalBase
{
    public override void Speak() { Quack(); }

    private void Quack()
    {
        Console.WriteLine ("Quack! Quack!");
    }
}
}

이 질문은 개념이 아닌 코딩과 관련이 있습니다.

2

나는 lib kontraktor에서 다음을 수행했습니다.

public class Actor<SELF extends Actor> {
    public SELF self() { return (SELF)_self; }
}

서브 클래 싱 :

public class MyHttpAppSession extends Actor<MyHttpAppSession> {
   ...
}

적어도 이것은 현재 클래스 내에서 그리고 강력한 형식의 참조가있을 때 작동합니다. 다중 상속이 작동하지만 실제로 까다로워집니다. :)


1

나는 이것이 사람이 요구 한 것과 완전히 다른 것을 안다. 이것을 해결하는 또 다른 방법은 반성입니다. 이것은 Generics의 이점을 얻지 못하지만 어떤 방식 으로든 유형 캐스팅을 처리하지 않고 수행하려는 동작 (개 껍질 만들기, 오리 멍청이 만들기 등)을 에뮬레이션 할 수 있습니다.

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

abstract class AnimalExample {
    private Map<String,Class<?>> friends = new HashMap<String,Class<?>>();
    private Map<String,Object> theFriends = new HashMap<String,Object>();

    public void addFriend(String name, Object friend){
        friends.put(name,friend.getClass());
        theFriends.put(name, friend);
    }

    public void makeMyFriendSpeak(String name){
        try {
            friends.get(name).getMethod("speak").invoke(theFriends.get(name));
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    } 

    public abstract void speak ();
};

class Dog extends Animal {
    public void speak () {
        System.out.println("woof!");
    }
}

class Duck extends Animal {
    public void speak () {
        System.out.println("quack!");
    }
}

class Cat extends Animal {
    public void speak () {
        System.out.println("miauu!");
    }
}

public class AnimalExample {

    public static void main (String [] args) {

        Cat felix = new Cat ();
        felix.addFriend("Spike", new Dog());
        felix.addFriend("Donald", new Duck());
        felix.makeMyFriendSpeak("Spike");
        felix.makeMyFriendSpeak("Donald");

    }

}

1

이건 어떤가요

public class Animal {
private Map<String,<T extends Animal>> friends = new HashMap<String,<T extends Animal>>();

public <T extends Animal> void addFriend(String name, T animal){
    friends.put(name,animal);
}

public <T extends Animal> T callFriend(String name){
    return friends.get(name);
}

}


0

또 다른 접근법이 있습니다. 메소드를 재정의 할 때 반환 유형을 좁힐 수 있습니다. 각 서브 클래스에서 해당 서브 클래스를 리턴하려면 callFriend를 대체해야합니다. 비용은 callFriend의 여러 선언이지만, 공통 부분을 내부적으로 호출되는 메소드로 분리 할 수 ​​있습니다. 이것은 위에서 언급 한 솔루션보다 훨씬 간단 해 보이며 반환 유형을 결정하기 위해 추가 인수가 필요하지 않습니다.


"반품 유형을 좁히십시오"라는 것이 무슨 뜻인지 잘 모르겠습니다. Afaik, Java 및 대부분의 유형이 지정된 언어는 반환 유형에 따라 메서드 또는 함수를 오버로드하지 않습니다. 예를 들어 컴파일러의 관점 public int getValue(String name){}과 구별 할 수 없습니다 public boolean getValue(String name){}. 과부하를 인식하려면 파라미터 유형을 변경하거나 파라미터를 추가 / 제거해야합니다. 어쩌면 난 그냥 ...하지만 당신을 오해하고있어
유일한 참 보습 바로 앞에 달린 풀 베는 날

Java에서는 서브 클래스의 메소드를 대체하고보다 "좁은"(즉,보다 구체적인) 리턴 유형을 지정할 수 있습니다. stackoverflow.com/questions/14694852/…를 참조하십시오 .
FeralWhippet

-3
public <X,Y> X nextRow(Y cursor) {
    return (X) getRow(cursor);
}

private <T> Person getRow(T cursor) {
    Cursor c = (Cursor) cursor;
    Person s = null;
    if (!c.moveToNext()) {
        c.close();
    } else {
        String id = c.getString(c.getColumnIndex("id"));
        String name = c.getString(c.getColumnIndex("name"));
        s = new Person();
        s.setId(id);
        s.setName(name);
    }
    return s;
}

모든 유형을 반환하고 직접받을 수 있습니다. 캐스트 할 필요가 없습니다.

Person p = nextRow(cursor); // cursor is real database cursor.

실제 커서 대신 다른 종류의 레코드를 사용자 정의하려는 경우이 방법이 가장 좋습니다.


2
(Cursor) cursor예를 들어 , "typecast 할 필요가 없다"고 말하지만 분명히 typecasting이 있습니다.
Sami Laine

이것은 완전히 부적절한 제네릭 사용입니다. 이 코드는 cursor이어야하며 Cursor항상 Person(또는 null)을 반환합니다 . 제네릭을 사용하면 이러한 제약 조건 검사가 제거되어 코드가 안전하지 않습니다.
Andy Turner
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.