2014-10-09 18 views
5

Tôi có hai cấu trúc phân loại đẳng cấu. Loại cơ sở của loại đầu tiên là BaseA và loại cơ sở của loại thứ hai là BaseB. Tôi biết làm thế nào để chuyển đổi bất kỳ đối tượng của bất kỳ phân lớp của BaseB để subtype tương ứng của nó của BaseA. Tôi muốn thực hiện một phương thức lấy đối tượng kiểu BaseB xác định lớp của nó và xây dựng một đối tượng của kiểu con tương ứng của BaseA. Mã ví dụ:Generics Java: <B mở rộng BaseB> không khớp <? mở rộng BaseB>

public interface BaseA... 
public interface BaseB... 
public class DerA implements BaseA... 
public class DerB implements BaseB... 
... 
public interface Transform<A,B> { 
    A toA (B b); 
} 

public class DerAtoDerB implements Transform<DerA,DerB> { 
    DerA toA (DerB b){...} 
} 

public class Transformations { 
    private static Map<Class<?>, Transform<? extends BaseA, ? extends BaseB>> _map = 
     new HashMap<>(); 
static { 
    _map.put(DerB.class, new DerAtoDerB()); 
    } 

public static <B extends BaseB> BaseA transform(B b){ 
    Transform<? extends BaseA, ? extends BaseB> t = _map.get(b.getClass()); 
    return t.toA(b); // Compile error: Transform<A,B#2> cannot be applied to given types 
} 

Tại sao <B extends BaseB> không tương thích với <? extends BaseB>? Ngoài ra nếu tôi cố gắng thực hiện chuyển đổi phương pháp tĩnh như thế này:

public static BaseA transform(BaseB b){ 
    Transform<? extends BaseA, ? extends BaseB> t = _map.get(b.getClass()); 
    return t.toA(b); // Compile error: Transform<A,B> cannot be applied to given types 
} 

tôi nhận được một lỗi biên dịch: Transform<A,B> cannot be applied to given types

bất cứ ai có thể giải thích cho tôi những gì tôi đang làm sai với Generics?

+0

câu trả lời đầu tiên có thể hữu ích cho bạn http://stackoverflow.com/questions/16449799/how-do-generics-of-generics-work – aalku

Trả lời

-2

Khái niệm chính về Generics Java: nếu ChildClass mở rộng ParentClass nó KHÔNG có nghĩa là YourApi < ChildClass> mở rộng YourApi < ParentClass>. Ví dụ:

NumberTransform<String, ? extends Number> intTransform = new IntegerTransform<String, Integer>(); // work with Integer numbers only 
NumberTransform<String, ? extends Number> longTransform = new LongTransform<String, Long>();  // work with Long numbers only 

longTransform.toA((Integer) 1); // you are trying to make this and got compilation error. 

Để giúp trình biên dịch thay thế t khởi của bạn:

Transform<? extends BaseA, B> t = (Transform<? extends BaseA, B>) _map.get(b.getClass()); 
+0

Làm thế nào để trả lời câu hỏi này? Điều này đúng hơn là một bình luận. –

+0

Thực ra tôi không cố gắng làm điều này, bởi vì việc thực hiện cụ thể Chuyển đổi được xác định bởi lớp của đối số, tức là. Tôi sẽ không vượt qua Integer để một cái gì đó mà mong đợi Long. – egelev

+0

bạn viết "Transform t = ...". một phần "? mở rộng BaseB" có nghĩa là nó có thể là BẤT K sub loại phụ của BaseB. Nó có thể là DerB, FooB, LambdaB, vv ... sau đó bạn chuyển "B b" cụ thể vào biến áp, từ quan điểm của trình biên dịch có thể là bất kỳ Biến áp <..., DerB>, Biến áp <..., FooB>, v.v. – ursa

0

Khi trình biên dịch gặp một biến với một ký tự đại diện trong loại của nó nó biết rằng có phải đã có một số T phù hợp với những gì đã được gửi in Nó không biết loại T đại diện, nhưng nó có thể tạo một trình giữ chỗ cho loại đó để tham chiếu đến kiểu mà T phải là. Trình giữ chỗ đó được gọi là bắt giữ ký tự đại diện cụ thể đó.

Tôi không biết lý do tại sao trình biên dịch không thể tìm ra rằng capture<? extends BaseB> có thể là capture<?> extends BaseB, có thể là một cái gì đó với loại tẩy xoá?

tôi thay vào đó sẽ thực hiện nó như thế này:

interface BaseA {} 
interface BaseB {} 
class DerA implements BaseA {} 
class DerB implements BaseB {} 

interface Transform { 
    BaseA toA(BaseB b); 
} 

class DerAtoDerB implements Transform { 
    public BaseA toA(BaseB b) { return new DerA(); } 
} 

class Transformations { 
    private static Map<Class<?>, Transform> _map = 
      new HashMap<>(); 

    static { 
     _map.put(DerB.class, new DerAtoDerB()); 
    } 

    public static<B extends BaseB> BaseA transform(B b) { 
     Transform t = _map.get(b.getClass()); 
     return t.toA(b); 
    } 
} 
+1

Không pha trộn các loại thô với thuốc generic. Phải có một giải pháp sạch hơn. :) –

0

? có nghĩa là loại không xác định.

Khi một biến là loại X bạn có thể gán cho nó một giá trị của loại X hoặc bất kỳ subtype của X nhưng "? extends X" có nghĩa là cái gì khác.

Điều đó có nghĩa là có loại không xác định có thể là X hoặc bất kỳ loại phụ nào là X. Nó không phải là điều tương tự.

Ví dụ:

public static Transform<? extends BaseA, ? extends BaseB> getSomething(){ 
    // My custom method 
    return new Transform<MySubclassOfA, MySubclassOfB>(); // <-- It does not accept BaseB, only MySubclassOfB 
} 
public static BaseA transform(BaseB b){ 
    Transform<? extends BaseA, ? extends BaseB> t = getSomething(); 
    return t.toA(b); // <--- THIS IS WRONG, it cannot accept any BaseB, only MySubclassOfB 
} 

Trong ví dụ trình biên dịch không biết nếu t thừa nhận bất kỳ BaseB hoặc những gì nhưng tôi thấy một ví dụ nơi nó không.

0

điều này biên dịch:

package com.test; 

import java.util.HashMap; 
import java.util.Map; 

interface BaseA{} 
interface BaseB{} 
class DerA implements BaseA{} 
class DerB implements BaseB{} 

interface Transform<A,B> { 
    A toA (B b); 
} 

class DerAtoDerB implements Transform<BaseA,BaseB> { 
    public DerA toA(DerB b){ return null; } 

    @Override 
    public BaseA toA(BaseB baseB) { 
     return null; 
    } 
} 

public class Transformations { 
    private static Map<Class<?>, Transform<? extends BaseA, ? super BaseB>> _map = new HashMap<Class<?>, Transform<? extends BaseA, ? super BaseB>>(); 
    static { 
     _map.put(DerB.class, new DerAtoDerB()); 
    } 

    public static <B extends BaseB> BaseA transform(B b){ 
     Transform<? extends BaseA, ? super BaseB> t = _map.get(b.getClass()); 
     return t.toA(b); 
    } 
} 

Những thay đổi tôi thực hiện cho mã của bạn như sau:

  1. DerAtoDerB nay thực hiện Transform<BaseA,BaseB>, thay vì Transform<DerA,DerB>
  2. Loại tham số chung thứ hai của Map đã thay đổi thành Transform<? extends BaseA, ? super BaseB> - chú ý sử dụng super thay vì extends - đó là loại đối diện bị ràng buộc.
2

Vấn đề là trong phương pháp transform trình biên dịch không thể biết rằng các loại tham số B extends BaseB và tham số Loại thứ hai trong lớp Transform (? extends BaseB) đã được nhận được từ bản đồ thực sự đại diện cho cùng lớp con của BaseB. Không có gì ngăn bạn từ lưu trữ một loại không tương thích trong bản đồ:

_map.put(DerB.class, new AnotherDerAtoAnotherDerB()); // the types don't match 

Bạn là một trong những người đảm bảo rằng các loại trong trận đấu bản đồ, vì vậy bạn cần phải nói với trình biên dịch bởi đúc nó vào đúng loại:

@SuppressWarnings("unchecked") 
public static <B extends BaseB> BaseA transform(B b) { 
    Transform<? extends BaseA, B> t = 
    (Transform<? extends BaseA, B>)_map.get(b.getClass()); 
    return t.toA(b); 
} 
Các vấn đề liên quan