메서드 반환 유형을 범용으로 만들려면 어떻게 해야 합니까?
이 예(OOP 서적의 일반적인 예)를 검토해 주십시오.
는 i i나 an an an i i i i i i i 。Animal
서 각 " " ", "'', ""는Animal
을 사용하다
리리esclassclassclass 같은 Dog
,Duck
,Mouse
행동을 추가하는 등bark()
,quack()
syslog.
여기 있어요.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
또는 적어도 더미 인스턴스 대신 유형의 클래스를 전달함으로써.
제네릭스는 컴파일 타임 타입 체크용이라고 알고 있습니다만, 회피책이 있습니까?
하면 될 것 같아요.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();
이 코드는 컴파일러 경고를 생성하지 않는 장점이 있습니다.물론 이것은 일반화 이전의 최신 버전의 캐스팅일 뿐이며 안전성을 더하지는 않습니다.
인지 알. 컴파일러는 어떤 타입인지 알 수 없습니다.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();
다음과 같이 구현할 수 있습니다.
@SuppressWarnings("unchecked")
public <T extends Animal> T callFriend(String name) {
return (T)friends.get(name);
}
(네, 이것은 법률 코드입니다.Java Generics: 범용 타입은 반환 타입으로만 정의됩니다.
이치노 주의해 주세요.@SuppressWarnings
주석: 이 코드가 활자화되지 않았음을 나타냅니다.직접 확인하셔야 합니다.그렇지 않으면ClassCastExceptions
행행시 。
유감스럽게도 (반환값을 임시변수에 할당하지 않고) 컴파일러를 만족시키는 유일한 방법은 다음과 같이 호출하는 것입니다.
jerry.<Dog>callFriend("spike").bark();
캐스팅보다 더 수도 , 캐스팅보다 더 좋은 것을 이 더 입니다.Animal
인 것으로 talk()
데이비드 슈미트가 말한 방법대로요
이 질문은 Effective Java의 항목 29 "Typesafe 이기종 컨테이너 검토"와 매우 유사합니다.Laz의 답변은 Bloch의 솔루션에 가장 가깝습니다.단, 안전을 위해 put과 get 모두 Class 리터럴을 사용해야 합니다.시그니처는 다음과 같습니다.
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을 참조하십시오.
다음은 간단한 버전입니다.
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();
}
}
또한 이 방법으로 지정된 유형의 값을 반환하도록 메서드에 요청할 수 있습니다.
<T> T methodName(Class<T> var);
Oracle Java 설명서에서 더 많은 예제를 참조해 주세요.
당신이 수업을 통과해도 괜찮다고 말한 것처럼, 당신은 다음과 같이 쓸 수 있습니다.
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 Token을 사용하여 Typesafe Hetherogentic Containers(THC)를 구현하는 방법이 있지만, 이 방법에는 또 다른 문제가 있습니다.
Super Type Tokens와 동일한 아이디어를 기반으로 문자열 대신 사용할 입력 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에서 유형을 가져올 수 있음을 의미합니다.
Typed의 비교 및 해싱 방법을 구현해야 합니다.ID의 동일한 인스턴스 2개를 비교할 수 있는 경우에도 ID를 사용합니다.
"instance of를 사용하여 추가 매개 변수 없이 런타임에 반환 유형을 계산하는 방법이 있습니까?"
대체 솔루션으로는 다음과 같은 방문자 패턴을 사용할 수 있습니다.Animal을 추상화하고 구현할 수 있도록 합니다.
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); }
}
여기서 트릭은 도그가 어떤 유형인지 알고 있기 때문에 "this"를 매개 변수로 전달함으로써 방문자 v의 관련 오버로드된 방문 방법을 트리거할 수 있다는 것입니다.다른 서브클래스도 똑같이 accept()를 구현합니다.
서브클래스 고유의 메서드를 호출하는 클래스는 다음과 같이 Visitor 인터페이스를 구현해야 합니다.
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 언어도 동일하게 작동합니다.
안 돼.스트링 키만 주어진다면 맵이 어떤 동물의 서브클래스를 얻을지 어떻게 알 수 있죠?
이것이 가능한 유일한 방법은 각 Animal이 1종류의 친구만 받아들이거나(Animal 클래스의 파라미터일 수도 있음), 또는 callFriend() 메서드의 type 파라미터를 얻는 것입니다.하지만 상속의 요점을 놓치고 있는 것 같습니다.즉, 슈퍼클래스 메서드만을 사용할 경우 서브클래스를 균일하게 취급할 수 있다는 것입니다.
개념 실증, 지원 클래스 및 실행 시 슈퍼 타입 토큰을 어떻게 취득할 수 있는지를 보여주는 테스트 클래스를 포함한 기사를 작성했습니다.즉, 발신자가 전달한 실제 범용 파라미터에 따라 대체 구현에 위임할 수 있습니다.예제:
TimeSeries<Double>
「」를 사용하는 합니다.double[]
TimeSeries<OHLC>
「」를 사용하는 합니다.ArrayList<OHLC>
참조:
- TypeTokens를 사용한 범용 파라미터 취득(Web 아카이브)
- TypeTokens를 사용한 범용 파라미터 취득(블로그)
고마워요.
Richard Gomes - 블로그
여기에는 많은 훌륭한 답변이 있지만, 이것은 Appium 테스트에서 채택한 접근법입니다.이 테스트에서는, 유저의 설정에 근거해 1개의 요소에 조작하면, 다른 애플리케이션 상태로 이행할 수 있습니다.OP의 사례를 따르지는 않지만 누군가에게 도움이 되었으면 합니다.
public <T extends MobilePage> T tapSignInButton(Class<T> type) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//signInButton.click();
return type.getConstructor(AppiumDriver.class).newInstance(appiumDriver);
}
- Mobile Page는 유형이 확장되는 슈퍼 클래스입니다.즉, 그 자식은 모두 사용할 수 있습니다(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;
}
그렇지 않습니다. 왜냐하면 컴파일러는 callFriend()가 Dog나 Duck이 아닌 Animal을 반환하고 있다는 것만 알고 있기 때문입니다.
Abstract makeNoise() 메서드를 Animal에 추가하여 그 서브클래스에 의해 바크 또는 쿼크로 구현할 수 없습니까?
여기서 찾고 있는 것은 추상화입니다.인터페이스에 대한 코드가 많아질수록 캐스팅을 줄일 수 있습니다.
다음 예는 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!");
}
}
}
내 lib kontraktor에서 다음을 수행했습니다.
public class Actor<SELF extends Actor> {
public SELF self() { return (SELF)_self; }
}
서브클래스:
public class MyHttpAppSession extends Actor<MyHttpAppSession> {
...
}
적어도 이것은 현재 클래스 내에서나 강력한 유형 참조가 있을 때 작동합니다.복수의 상속은 기능하지만, 그 후에는 매우 까다로워집니다. : )
나는 이것이 그 사람이 물어본 것과는 완전히 다른 것이라는 것을 안다.이 문제를 해결하는 또 다른 방법은 반영입니다.즉, 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");
}
}
질문은 가상 데이터에 기초하고 있기 때문에 비교 가능한 인터페이스를 확장한 범용 데이터를 반환하는 좋은 예가 있습니다.
public class MaximumTest {
// find the max value using Comparable interface
public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
T max = x; // assume that x is initially the largest
if (y.compareTo(max) > 0){
max = y; // y is the large now
}
if (z.compareTo(max) > 0){
max = z; // z is the large now
}
return max; // returns the maximum value
}
//testing with an ordinary main method
public static void main(String args[]) {
System.out.printf("Maximum of %d, %d and %d is %d\n\n", 3, 4, 5, maximum(3, 4, 5));
System.out.printf("Maximum of %.1f, %.1f and %.1f is %.1f\n\n", 6.6, 8.8, 7.7, maximum(6.6, 8.8, 7.7));
System.out.printf("Maximum of %s, %s and %s is %s\n", "strawberry", "apple", "orange",
maximum("strawberry", "apple", "orange"));
}
}
다른 방법이 있습니다. 메서드를 재정의할 때 반환 유형을 좁힐 수 있습니다.각 서브클래스에서 해당 서브클래스를 반환하려면 callFriend를 덮어써야 합니다.비용은 callFriend의 여러 선언이지만 공통 부분을 내부라고 하는 방법으로 분리할 수 있습니다.이것은 위에서 설명한 솔루션보다 훨씬 단순하다고 생각되며 반환 유형을 결정하기 위해 별도의 인수가 필요하지 않습니다.
어때
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);
}
}
언급URL : https://stackoverflow.com/questions/450807/how-do-i-make-the-method-return-type-generic
'programing' 카테고리의 다른 글
Vuetify 색상 테마 변경이 작동하지 않음 (0) | 2022.07.14 |
---|---|
ctime()에 의해 반환된 문자열에 줄바꿈이 포함되어 있는 이유는 무엇입니까? (0) | 2022.07.14 |
Completable의 차이점미래, 미래 및 RxJava의 관측 가능 (0) | 2022.07.14 |
RouteUpdate 이전 vue-router가 전환 시 이전 매개 변수를 사용함 (0) | 2022.07.12 |
주의: 어레이 첨자에 char 유형이 있습니다. (0) | 2022.07.12 |