有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

T具有不兼容的Java泛型边界

我试图通过创建一个泛型的CardDeck类来学习泛型,就像这样

卡片。java

package com.tn.deck;

public class Card<S extends Comparable<S>, R extends Comparable<R>> implements Comparable<Card<S, R>> {
    private S suit;
    private R rank;

    public Card(S suit, R rank) {
        this.suit = suit;
        this.rank = rank;
    }

    @Override
    public String toString() {
        return "Card{" +
                "suit=" + suit +
                ", rank=" + rank +
                '}';
    }

    @Override
    public int compareTo(Card<S, R> o) {
        return rank.compareTo(o.rank);
    }
}

甲板。java

package com.tn.deck;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;


public final class Deck<T extends Card> {
    private List<T> deck;

    public <S extends Comparable<S>, R extends Comparable<R>> Deck(int numberOfDecks, S[] suits, R[] ranks) {
        this.deck = initializeDecks(numberOfDecks, suits, ranks);
        shuffle();
    }


    private <S extends Comparable<S>, R extends Comparable<R>> List<T> initializeDecks(S[] suits, R[] ranks) {
        return Arrays.stream(suits)
                .flatMap(suit -> Arrays.stream(ranks).map(rank -> new Card<>(suit, rank)))
                .collect(Collectors.toList()); // === It complains here. ===
    }

    private <S extends Comparable<S>, R extends Comparable<R>> List<T> initializeDecks(int numberOfDecks, S[] suits, R[] ranks) {
        return IntStream.range(0, numberOfDecks)
                .mapToObj(i -> initializeDecks(suits, ranks))
                .flatMap(Collection::stream)
                .collect(Collectors.toList()); 
    }

    public void shuffle() {
        Collections.shuffle(deck);
    }

    @Override
    public String toString() {
        return "Deck{" +
                "deck=" + deck +
                '}';
    }
}

我得到的错误是

Error:(57, 25) java: incompatible types: inference variable T has incompatible bounds
    equality constraints: T
    lower bounds: com.tn.deck.Card<S,R>

可能只是我不懂泛型,但我真的很感激有人能向我解释我做错了什么


共 (3) 个答案

  1. # 1 楼答案

    在产生错误的方法中,您创建了一组Card<S, R>对象,然后试图将它们放入List中。为了实现这一点,List需要能够保存该类型的对象

    当且仅当对象的类是TT的子类时,List<T>可以保存对象。所以,为了让这行工作,Card<S, R>必须是T的子类。现在,如果你看一下T上的约束,你指定的是TCard的一个子类。此约束不能保证将卡放入列表所需的关系,因此会出现错误

  2. # 2 楼答案

    为什么会出现这种错误?泛型参数T extends CardCard的所有子类,包括它本身,在这里是允许的。当泛型参数TCard的子类类型时,当代码initializeDecks尝试返回超类实例而不强制转换时,就会发生错误。让我们看一个简单的具体例子:

    class Superclass{}
    class Subclass extends Superclass{}
    
    Subclass sub= ...;
    // you can assign subclass's instance to superclass since it is-a superclass
    Superclass sup = sub;
    // you can assign superclass's instance to subclass you need casting it down
    Subclass sub2 = (Superclass) sup;
    

    您可以按如下方式修复代码:

    <S extends Comparable<S>, R extends Comparable<R>> 
            List<? super T> initializeDecks(...){ 
      ....
    }
    
  3. # 3 楼答案

    这是因为您正在创建Card,但必须返回List<T>。而且T可以是Card的任何亚型,而不是Card本身

    当您使用泛型类并需要创建T(或任何已删除的泛型类型)的新实例时,您应该接受一个能够这样做的工厂。例如:

    public <S extends Comparable<S>, R extends Comparable<R>> Deck(int numberOfDecks, S[] suits, R[] ranks, BiFunction<S, R, T> cardFactory) {
        this.deck = initializeDecks(numberOfDecks, suits, ranks, cardFactory);
        shuffle();
    }
    
    
    private <S extends Comparable<S>, R extends Comparable<R>> List<T> initializeDecks(S[] suits, R[] ranks, BiFunction<S, R, T> cardFactory) {
        return Arrays.stream(suits)
                .flatMap(suit -> Arrays.stream(ranks).map(rank -> cardFactory.apply(suit, rank)))
                .collect(Collectors.toList());
    }
    
    private <S extends Comparable<S>, R extends Comparable<R>> List<T> initializeDecks(int numberOfDecks, S[] suits, R[] ranks, BiFunction<S, R, T> cardFactory) {
        return IntStream.range(0, numberOfDecks)
                .mapToObj(i -> initializeDecks(suits, ranks, cardFactory))
                .flatMap(Collection::stream)
                .collect(Collectors.toList());
    }