Làm thế nào để tôi giải quyết các cảnh báo diễn viên không được kiểm tra?


611

Eclipse đang cho tôi một cảnh báo về hình thức sau:

Loại an toàn: Bỏ chọn truyền từ Object sang HashMap

Đây là từ một cuộc gọi đến API mà tôi không có quyền kiểm soát nào trả về Object:

HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
  HashMap<String, String> theHash = (HashMap<String, String>)session.getAttribute("attributeKey");
  return theHash;
}

Tôi muốn tránh các cảnh báo của Eclipse, nếu có thể, vì về mặt lý thuyết, chúng chỉ ra ít nhất một vấn đề về mã tiềm năng. Tôi chưa tìm thấy một cách tốt để loại bỏ cái này. Tôi có thể tự trích xuất một dòng liên quan đến một phương thức và thêm @SuppressWarnings("unchecked")vào phương thức đó, do đó hạn chế tác động của việc có một khối mã mà tôi bỏ qua các cảnh báo. Còn lựa chọn nào tốt hơn không? Tôi không muốn tắt những cảnh báo này trong Eclipse.

Trước khi tôi đến mã, nó đơn giản hơn, nhưng vẫn gây ra cảnh báo:

HashMap getItems(javax.servlet.http.HttpSession session) {
  HashMap theHash = (HashMap)session.getAttribute("attributeKey");
  return theHash;
}

Vấn đề là ở nơi khác khi bạn cố gắng sử dụng các hash bạn sẽ nhận được cảnh báo:

HashMap items = getItems(session);
items.put("this", "that");

Type safety: The method put(Object, Object) belongs to the raw type HashMap.  References to generic type HashMap<K,V> should be parameterized.

Nếu bạn đang sử dụng HttpSession như thế, hãy xem bài viết của Brian Goetz về chủ đề này: ibm.com/developerworks/l Library / j
Tom Hawtin - tackline

Nếu một diễn viên không được kiểm soát là không thể tránh khỏi, một ý tưởng tốt là kết hợp chặt chẽ nó với một cái gì đó đại diện hợp lý cho loại của nó (như một enumhoặc thậm chí các trường hợp Class<T>), vì vậy bạn có thể liếc nhìn nó và biết rằng nó an toàn.
Philip Guin



Tôi sẽ thêm, tôi thấy tôi chỉ có thể thêm @SuppressWarnings ("không được kiểm tra") ở cấp phương thức có chứa mã vi phạm. Vì vậy, tôi đã phá vỡ mã ra một thói quen mà tôi phải làm điều này. Tôi luôn nghĩ rằng bạn có thể làm điều này ngay lập tức trên dòng trong câu hỏi.
JGFMK

Câu trả lời:


557

Câu trả lời rõ ràng, tất nhiên, không phải là làm các diễn viên không được kiểm soát.

Nếu nó thực sự cần thiết, thì ít nhất hãy cố gắng giới hạn phạm vi của @SuppressWarningschú thích. Theo Javadocs của nó , nó có thể đi vào các biến cục bộ; bằng cách này, nó thậm chí không ảnh hưởng đến toàn bộ phương pháp.

Thí dụ:

@SuppressWarnings("unchecked")
Map<String, String> myMap = (Map<String, String>) deserializeMap();

Không có cách nào để xác định liệu Mapthực sự nên có các tham số chung <String, String>. Bạn phải biết trước các thông số sẽ là gì (hoặc bạn sẽ tìm ra khi bạn nhận được a ClassCastException). Đây là lý do tại sao mã tạo cảnh báo, bởi vì trình biên dịch không thể biết liệu có an toàn hay không.


112
+1 để chỉ ra rằng nó có thể đi vào các biến cục bộ. Eclipse chỉ cung cấp để thêm nó vào toàn bộ phương thức ...
thSoft

17
Eclipse 3.7 (Indigo) có hỗ trợ thêm không được kiểm tra vào các biến cục bộ.
sweetfa

78
Cảnh báo không chỉ vì trình biên dịch không biết rằng dàn diễn viên an toàn. Ví dụ, String s = (String) new Object() ;không có cảnh báo, mặc dù trình biên dịch không biết rằng việc truyền là an toàn. Cảnh báo là do trình biên dịch (a) không biết rằng cast là an toàn AND (b) sẽ không tạo ra một kiểm tra thời gian chạy hoàn chỉnh tại điểm của cast. Sẽ có một kiểm tra rằng đó là một Hashmap, nhưng sẽ không có kiểm tra rằng đó là một HashMap<String,String>.
Theodore Norvell

9
Đáng buồn thay, mặc dù diễn viên và cảnh báo là dành cho bài tập , chú thích phải tiếp tục khai báo biến ... Vì vậy, nếu phần khai báo và bài tập ở các vị trí khác nhau (giả sử, bên ngoài và bên trong khối 'thử' tương ứng) , Eclipse hiện tạo ra hai cảnh báo: diễn viên chưa được kiểm tra ban đầu và chẩn đoán "chú thích không cần thiết" mới.
Ti Strga

6
Một cách giải quyết cho chú thích cần phải đi kèm với khai báo biến cục bộ, có thể ở một phạm vi khác trên một dòng khác với diễn viên thực tế, là tạo một biến cục bộ trong phạm vi của diễn viên cụ thể để thực hiện truyền trên cùng một dòng như tuyên bố. Sau đó gán biến này cho biến thực trong phạm vi khác. Đây cũng là phương pháp tôi đã sử dụng để triệt tiêu cảnh báo trên một biến đối với một biến đối tượng vì chú thích cũng không thể được áp dụng ở đây.
Jeff Lockhart

168

Thật không may, không có lựa chọn tuyệt vời ở đây. Hãy nhớ rằng, mục tiêu của tất cả những điều này là để bảo vệ loại an toàn. " Java Generics " cung cấp một giải pháp để xử lý các thư viện kế thừa không chung chung và có một thứ đặc biệt gọi là "kỹ thuật vòng lặp rỗng" trong phần 8.2. Về cơ bản, làm cho các diễn viên không an toàn, và ngăn chặn cảnh báo. Sau đó lặp qua bản đồ như thế này:

@SuppressWarnings("unchecked")
Map<String, Number> map = getMap();
for (String s : map.keySet());
for (Number n : map.values());

Nếu gặp phải loại không mong muốn, bạn sẽ nhận được thời gian chạy ClassCastException, nhưng ít nhất nó sẽ xảy ra gần với nguồn gốc của vấn đề.


6
Câu trả lời hay hơn nhiều so với câu trả lời được cung cấp bởi Skiphoppy, vì nhiều lý do: 1) Mã này ngắn hơn nhiều. 2) Mã này thực sự ném ClassCastException như mong đợi. 3) Mã này không thực hiện một bản sao đầy đủ của bản đồ nguồn. 4) Các vòng lặp có thể dễ dàng được bọc trong một phương thức riêng biệt được sử dụng trong một xác nhận, điều này sẽ dễ dàng loại bỏ hiệu năng đạt được trong mã sản xuất.
Stijn de Witt

6
Không có khả năng trình biên dịch Java hay trình biên dịch JIT sẽ quyết định rằng kết quả của mã này không được sử dụng và "tối ưu hóa" nó bằng cách không biên dịch nó?
RenniePet

1
Nó không thực sự là mã chết nếu nó có khả năng ném ngoại lệ. Tôi không biết đủ về trình biên dịch JIT được sử dụng ngày hôm nay để đảm bảo rằng không ai trong số họ sẽ làm hỏng điều này, nhưng tôi cảm thấy khá tự tin khi nói rằng họ không được phép.
GrandOpener

3
Điều này vẫn không đảm bảo an toàn kiểu như cùng một bản đồ vẫn đang được sử dụng. Ban đầu, nó có thể được định nghĩa là Map <Object, Object> chỉ có Chuỗi và Số và sau đó nếu Boolean được thêm vào thì người dùng mã này sẽ gặp khó khăn và khó theo dõi bất ngờ. Cách duy nhất để đảm bảo an toàn loại là sao chép nó vào một bản đồ mới với loại được yêu cầu đảm bảo những gì được phép đi vào đó.
dùng2219808

112

Ồ; Tôi nghĩ rằng tôi đã tìm ra câu trả lời cho câu hỏi của riêng tôi. Tôi chỉ không chắc là nó có giá trị! :)

Vấn đề là dàn diễn viên không được kiểm tra. Vì vậy, bạn phải tự kiểm tra nó. Bạn không thể chỉ kiểm tra một loại tham số hóa với instanceof, bởi vì thông tin loại được tham số hóa không có sẵn trong thời gian chạy, đã bị xóa trong thời gian biên dịch.

Tuy nhiên, bạn có thể thực hiện kiểm tra từng mục trong hàm băm, với thể hiện, và khi làm như vậy, bạn có thể tạo một hàm băm mới an toàn kiểu. Và bạn sẽ không kích động bất kỳ cảnh báo.

Nhờ mmyer và Esko Luontola, tôi đã tham số hóa mã ban đầu tôi đã viết ở đây, vì vậy nó có thể được gói trong một lớp tiện ích ở đâu đó và được sử dụng cho bất kỳ HashMap tham số nào. Nếu bạn muốn hiểu rõ hơn về nó và không quen thuộc với thuốc generic, tôi khuyến khích xem lịch sử chỉnh sửa của câu trả lời này.

public static <K, V> HashMap<K, V> castHash(HashMap input,
                                            Class<K> keyClass,
                                            Class<V> valueClass) {
  HashMap<K, V> output = new HashMap<K, V>();
  if (input == null)
      return output;
  for (Object key: input.keySet().toArray()) {
    if ((key == null) || (keyClass.isAssignableFrom(key.getClass()))) {
        Object value = input.get(key);
        if ((value == null) || (valueClass.isAssignableFrom(value.getClass()))) {
            K k = keyClass.cast(key);
            V v = valueClass.cast(value);
            output.put(k, v);
        } else {
            throw new AssertionError(
                "Cannot cast to HashMap<"+ keyClass.getSimpleName()
                +", "+ valueClass.getSimpleName() +">"
                +", value "+ value +" is not a "+ valueClass.getSimpleName()
            );
        }
    } else {
        throw new AssertionError(
            "Cannot cast to HashMap<"+ keyClass.getSimpleName()
            +", "+ valueClass.getSimpleName() +">"
            +", key "+ key +" is not a " + keyClass.getSimpleName()
        );
    }
  }
  return output;
}

Đó là rất nhiều công việc, có thể là rất ít phần thưởng ... Tôi không chắc liệu tôi có sử dụng nó hay không. Tôi đánh giá cao bất kỳ ý kiến ​​nào về việc mọi người nghĩ rằng nó có giá trị hay không. Ngoài ra, tôi đánh giá cao các đề xuất cải tiến: tôi có thể làm gì tốt hơn ngoài việc ném Ass AssErrors không? Có cái gì tốt hơn tôi có thể ném? Tôi có nên làm cho nó một ngoại lệ được kiểm tra?


68
công cụ này gây nhầm lẫn, nhưng tôi nghĩ rằng tất cả những gì bạn đã làm là giao dịch ClassCastExceptions cho AssertsErrors.
Dustin Getz

59
Anh bạn, điều đó chắc chắn không đáng! Hãy tưởng tượng nhựa nghèo, người phải quay lại và sửa đổi một số mã với mớ hỗn độn đó. Tôi không thích đàn áp những cảnh báo, nhưng tôi nghĩ đó là điều xấu xa hơn ở đây.
Craig B

69
Nó không chỉ là một thứ xấu xí, khó hiểu, lộn xộn (khi bạn không thể tránh được một bình luận đa dạng có thể dẫn người lập trình bảo trì đi qua nó); lặp đi lặp lại trên mọi phần tử trong bộ sưu tập sẽ chuyển cast từ hoạt động O (1) sang hoạt động O (n). Đây là điều sẽ không bao giờ được mong đợi và có thể dễ dàng biến thành một sự chậm chạp bí ẩn khủng khiếp.
Dan đang loay hoay bởi Firelight

22
@DanNeely bạn đúng. Nói chung, không ai nên làm điều này.
Skiphoppy

4
Một số ý kiến ​​... chữ ký phương thức là sai vì nó không "bỏ" một thứ chết tiệt, nó chỉ sao chép bản đồ hiện có vào một bản đồ mới. Ngoài ra, nó có thể được tái cấu trúc để chấp nhận bất kỳ bản đồ nào và không dựa vào chính HashMap (tức là lấy Map và trả về Map trong chữ ký phương thức, ngay cả khi kiểu bên trong là HashMap). Bạn thực sự không cần phải thực hiện việc truyền hoặc lưu trữ vào một bản đồ mới - nếu bạn không đưa ra một lỗi xác nhận, thì bản đồ đã cho có đúng loại bên trong nó ngay bây giờ. Tạo một bản đồ mới với các loại chung là vô nghĩa vì bạn vẫn có thể làm cho nó trở nên thô và đặt bất cứ thứ gì.
MetroidFan2002

51

Trong Tùy chọn Eclipse, đi tới Java-> Trình biên dịch-> Lỗi / Cảnh báo-> Các loại chung và chọn Ignore unavoidable generic type problemshộp kiểm.

Điều này thỏa mãn mục đích của câu hỏi, tức là

Tôi muốn tránh các cảnh báo của Eclipse ...

nếu không phải là tinh thần.


1
À, cảm ơn vì điều này :) Tôi đã gặp uses unchecked or unsafe operations.lỗi "" javac, nhưng việc thêm vào @SuppressWarnings("unchecked")đã khiến Eclipse không hài lòng, cho rằng việc đàn áp là không cần thiết. Bỏ chọn hộp này làm cho Eclipse và javachoạt động giống nhau, đó là điều tôi muốn. Hoàn toàn triệt tiêu cảnh báo trong mã rõ ràng hơn nhiều so với triệt tiêu nó ở mọi nơi trong Eclipse.
dimo414

26

Bạn có thể tạo một lớp tiện ích như sau và sử dụng nó để loại bỏ cảnh báo không được kiểm tra.

public class Objects {

    /**
     * Helps to avoid using {@code @SuppressWarnings({"unchecked"})} when casting to a generic type.
     */
    @SuppressWarnings({"unchecked"})
    public static <T> T uncheckedCast(Object obj) {
        return (T) obj;
    }
}

Bạn có thể sử dụng nó như sau:

import static Objects.uncheckedCast;
...

HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
      return uncheckedCast(session.getAttribute("attributeKey"));
}

Một số thảo luận thêm về điều này có tại đây: http://cleveralias.bloss.com/ Dùt_spearmints / 2006/01 / suppresswarning.html


18
không bỏ qua, nhưng trình bao bọc thêm chính xác không có gì hơn là chỉ áp dụng cảnh báo.
Dustin Getz

3
+1 vì giải pháp này không lãng phí các dòng mã quý giá.
Tino

1
@ErikE Quá nhiều. Những màn hình lớn hơn và có độ phân giải cao hơn đắt tiền hơn để nhường chỗ cho tất cả những dòng lãng phí đó, một bàn lớn hơn để đặt tất cả những màn hình lớn hơn đó, một phòng lớn hơn để đặt bàn lớn hơn và một ông chủ sâu sắc ..
Tino

1
@ErikE Thanh cuộn, để làm vigì? Bạn đang giỡn hả?
Tino

21

Những thứ này thật khó, nhưng đây là những suy nghĩ hiện tại của tôi:

Nếu API của bạn trả về Object, thì bạn không thể làm gì - dù thế nào, bạn sẽ bị mù một cách mù quáng đối tượng. Bạn để Java ném ClassCastExceptions hoặc bạn có thể tự kiểm tra từng phần tử và ném Assertions hoặc IllegalArgumentExceptions hoặc một số thứ như vậy, nhưng các kiểm tra thời gian chạy này đều tương đương. Bạn phải loại bỏ thời gian biên dịch không được kiểm tra, bất kể bạn làm gì trong thời gian chạy.

Tôi chỉ muốn phân loại mù và để JVM thực hiện kiểm tra thời gian chạy cho tôi vì chúng tôi "biết" API sẽ trả về cái gì và thường sẵn sàng cho rằng API hoạt động. Sử dụng thuốc generic ở khắp mọi nơi phía trên diễn viên, nếu bạn cần chúng. Bạn không thực sự mua bất cứ thứ gì ở đó vì bạn vẫn có một nhóm mù duy nhất, nhưng ít nhất bạn có thể sử dụng thuốc generic từ đó trở lên để JVM có thể giúp bạn tránh các lỗi mù trong các đoạn mã khác.

Trong trường hợp cụ thể này, có lẽ bạn có thể thấy lệnh gọi SetAttribution và thấy loại đang diễn ra, vì vậy chỉ cần bỏ qua kiểu này để cùng loại trên đường ra là không vô đạo đức. Thêm một bình luận tham chiếu SetAttribution và được thực hiện với nó.


16

Dưới đây là một ví dụ rút gọn để tránh cảnh báo "diễn viên không được kiểm soát" bằng cách sử dụng hai chiến lược được đề cập trong các câu trả lời khác.

  1. Truyền xuống Class của loại sở thích dưới dạng tham số trong thời gian chạy ( Class<T> inputElementClazz). Sau đó, bạn có thể sử dụng:inputElementClazz.cast(anyObject);

  2. Để tạo kiểu cho Bộ sưu tập, hãy sử dụng ký tự đại diện? thay vì loại T chung chung để xác nhận rằng bạn thực sự không biết loại đối tượng nào mong đợi từ mã kế thừa ( Collection<?> unknownTypeCollection). Rốt cuộc, đây là điều mà cảnh báo "diễn viên không được kiểm soát" muốn nói với chúng tôi: Chúng tôi không thể chắc chắn rằng mình có được một Collection<T>, vì vậy điều trung thực cần làm là sử dụng a Collection<?>. Nếu thực sự cần thiết, một bộ sưu tập của một loại đã biết vẫn có thể được xây dựng ( Collection<T> knownTypeCollection).

Mã kế thừa được giao tiếp trong ví dụ bên dưới có thuộc tính "đầu vào" trong StructuredViewer (StructuredViewer là một tiện ích cây hoặc bảng, "đầu vào" là mô hình dữ liệu đằng sau nó). "Đầu vào" này có thể là bất kỳ loại Bộ sưu tập Java nào.

public void dragFinished(StructuredViewer structuredViewer, Class<T> inputElementClazz) {
    IStructuredSelection selection = (IStructuredSelection) structuredViewer.getSelection();
    // legacy code returns an Object from getFirstElement,
    // the developer knows/hopes it is of type inputElementClazz, but the compiler cannot know
    T firstElement = inputElementClazz.cast(selection.getFirstElement());

    // legacy code returns an object from getInput, so we deal with it as a Collection<?>
    Collection<?> unknownTypeCollection = (Collection<?>) structuredViewer.getInput();

    // for some operations we do not even need a collection with known types
    unknownTypeCollection.remove(firstElement);

    // nothing prevents us from building a Collection of a known type, should we really need one
    Collection<T> knownTypeCollection = new ArrayList<T>();
    for (Object object : unknownTypeCollection) {
        T aT = inputElementClazz.cast(object);
        knownTypeCollection.add(aT);
        System.out.println(aT.getClass());
    }

    structuredViewer.refresh();
}

Đương nhiên, mã ở trên có thể gây ra lỗi thời gian chạy nếu chúng ta sử dụng mã kế thừa với các loại dữ liệu sai (ví dụ: nếu chúng ta đặt một mảng là "đầu vào" của StructuredViewer thay vì Bộ sưu tập Java).

Ví dụ về cách gọi phương thức:

dragFinishedStrategy.dragFinished(viewer, Product.class);

13

Trong thế giới Phiên HTTP, bạn thực sự không thể tránh được việc sử dụng, vì API được viết theo cách đó (chỉ nhận và trả về Object ).

Với một chút công việc, bạn có thể dễ dàng tránh được các diễn viên không được kiểm soát, 'mặc dù. Điều này có nghĩa là nó sẽ biến thành một diễn viên truyền thống trao ClassCastExceptionquyền ở đó trong trường hợp xảy ra lỗi). Một ngoại lệ không được kiểm soát có thể biến thành CCEbất kỳ lúc nào sau đó thay vì điểm của diễn viên (đó là lý do tại sao đó là một cảnh báo riêng biệt).

Thay thế HashMap bằng một lớp chuyên dụng:

import java.util.AbstractMap;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Attributes extends AbstractMap<String, String> {
    final Map<String, String> content = new HashMap<String, String>();

    @Override
    public Set<Map.Entry<String, String>> entrySet() {
        return content.entrySet();
    }

    @Override
    public Set<String> keySet() {
        return content.keySet();
    }

    @Override
    public Collection<String> values() {
        return content.values();
    }

    @Override
    public String put(final String key, final String value) {
        return content.put(key, value);
    }
}

Sau đó, chuyển sang lớp đó thay vì Map<String,String>và mọi thứ sẽ được kiểm tra tại chính xác nơi bạn viết mã của mình. Không có bất ngờ ClassCastExceptionssau này.


Đây thực sự là câu trả lời hữu ích.
GPrathap

10

Trong Android Studio nếu bạn muốn tắt kiểm tra, bạn có thể sử dụng:

//noinspection unchecked
Map<String, String> myMap = (Map<String, String>) deserializeMap();

2
Điều này cũng hoạt động trong IntelliJ IDE
neXus

8

Trong trường hợp cụ thể này, tôi sẽ không lưu trữ Bản đồ trực tiếp vào HttpSession, mà thay vào đó là một thể hiện của lớp của riêng tôi, trong đó lần lượt chứa Bản đồ (một chi tiết triển khai của lớp). Sau đó, bạn có thể chắc chắn rằng các yếu tố trong bản đồ là đúng loại.

Nhưng nếu bạn muốn kiểm tra xem nội dung của Bản đồ có đúng loại hay không, bạn có thể sử dụng một mã như thế này:

public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("a", 1);
    map.put("b", 2);
    Object obj = map;

    Map<String, Integer> ok = safeCastMap(obj, String.class, Integer.class);
    Map<String, String> error = safeCastMap(obj, String.class, String.class);
}

@SuppressWarnings({"unchecked"})
public static <K, V> Map<K, V> safeCastMap(Object map, Class<K> keyType, Class<V> valueType) {
    checkMap(map);
    checkMapContents(keyType, valueType, (Map<?, ?>) map);
    return (Map<K, V>) map;
}

private static void checkMap(Object map) {
    checkType(Map.class, map);
}

private static <K, V> void checkMapContents(Class<K> keyType, Class<V> valueType, Map<?, ?> map) {
    for (Map.Entry<?, ?> entry : map.entrySet()) {
        checkType(keyType, entry.getKey());
        checkType(valueType, entry.getValue());
    }
}

private static <K> void checkType(Class<K> expectedType, Object obj) {
    if (!expectedType.isInstance(obj)) {
        throw new IllegalArgumentException("Expected " + expectedType + " but was " + obj.getClass() + ": " + obj);
    }
}

1
Tuyệt vời; Tôi nghĩ rằng tôi có thể kết hợp điều đó với câu trả lời của mình để tham số hóa nó và tránh phải loại bỏ hoàn toàn các cảnh báo!
Skiphoppy

1
+1 có thể là công thức tốt nhất (dễ hiểu và duy trì) để thực hiện một cách an toàn với kiểm tra thời gian chạy
Tino

8

Hàm tiện ích Object.Unchecked trong câu trả lời ở trên của Esko Luontola là một cách tuyệt vời để tránh sự lộn xộn của chương trình.

Nếu bạn không muốn SuppressWarnings trên toàn bộ phương thức, Java buộc bạn phải đặt nó trên một cục bộ. Nếu bạn cần chọn một thành viên, nó có thể dẫn đến mã như thế này:

@SuppressWarnings("unchecked")
Vector<String> watchedSymbolsClone = (Vector<String>) watchedSymbols.clone();
this.watchedSymbols = watchedSymbolsClone;

Sử dụng tiện ích sạch hơn nhiều, và rõ ràng bạn đang làm gì:

this.watchedSymbols = Objects.uncheckedCast(watchedSymbols.clone());

LƯU Ý: Tôi cảm thấy rất quan trọng khi thêm rằng đôi khi cảnh báo thực sự có nghĩa là bạn đang làm sai điều gì đó như:

ArrayList<Integer> intList = new ArrayList<Integer>();
intList.add(1);
Object intListObject = intList; 

 // this line gives an unchecked warning - but no runtime error
ArrayList<String> stringList  = (ArrayList<String>) intListObject;
System.out.println(stringList.get(0)); // cast exception will be given here

Điều mà trình biên dịch đang nói với bạn là việc cast này sẽ KHÔNG được kiểm tra trong thời gian chạy, do đó không có lỗi thời gian chạy nào được đưa ra cho đến khi bạn cố gắng truy cập dữ liệu trong vùng chứa chung.


5

Cảnh báo đàn áp không phải là một giải pháp. Bạn không nên thực hiện hai cấp độ đúc trong một tuyên bố.

HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {

    // first, cast the returned Object to generic HashMap<?,?>
    HashMap<?, ?> theHash = (HashMap<?, ?>)session.getAttribute("attributeKey");

    // next, cast every entry of the HashMap to the required type <String, String>
    HashMap<String, String> returingHash = new HashMap<>();
    for (Entry<?, ?> entry : theHash.entrySet()) {
        returingHash.put((String) entry.getKey(), (String) entry.getValue());
    }
    return returingHash;
}

1
Câu hỏi năm tuổi của anh ấy? Bạn có cần phải làm nhiều việc không? Đã cho Java có kiểu xóa, hashmap thứ hai phải giống hệt với lần đầu tiên trong thời gian chạy; Tôi nghĩ rằng nó sẽ hiệu quả hơn và tránh việc sao chép nếu bạn chỉ lặp qua các mục và xác minh rằng chúng là tất cả các phiên bản của chuỗi. Hoặc, TBH, kiểm tra nguồn của JAR servlet mà bạn đang sử dụng và xác minh nó chỉ đặt chuỗi.
Rupi

1
Cho đến hôm nay tôi đang thấy cảnh báo này trong các dự án. Vấn đề của anh không phải là xác minh loại, mà là một cảnh báo do "đặt" vào bản đồ không được chiếu.
abbas

2

Đoán nhanh nếu bạn đăng mã của bạn có thể nói chắc chắn nhưng bạn có thể đã làm gì đó dọc theo dòng

HashMap<String, Object> test = new HashMap();

Điều này sẽ tạo ra cảnh báo khi bạn cần làm

HashMap<String, Object> test = new HashMap<String, Object>();

nó có thể đáng xem

Generics trong ngôn ngữ lập trình Java

nếu bạn không quen thuộc với những gì cần phải làm.


1
Thật không may, đó không phải là một tình huống dễ dàng. Mã được thêm vào.
Skiphoppy

1
Tôi đến đây để tìm câu trả lời cho một vấn đề hơi khác: và bạn nói với tôi chính xác những gì tôi cần! Cảm ơn!
staticsan

2

Tôi có thể đã hiểu nhầm câu hỏi (một ví dụ và một vài dòng xung quanh sẽ rất hay), nhưng tại sao bạn không luôn sử dụng một giao diện thích hợp (và Java5 +)? Tôi thấy không có lý do tại sao bạn muốn đúc HashMapthay vì a Map<KeyType,ValueType>. Trong thực tế, tôi không thể tưởng tượng bất kỳ lý do nào để đặt loại biến HashMapthay vì Map.

Và tại sao nguồn là một Object? Đây có phải là một loại tham số của một bộ sưu tập di sản? Nếu vậy, sử dụng thuốc generic và chỉ định loại bạn muốn.


2
Tôi khá chắc chắn rằng việc chuyển sang Map trong trường hợp này sẽ không thay đổi bất cứ điều gì, nhưng cảm ơn vì mẹo lập trình, có thể thay đổi cách tôi làm một số thứ, để tốt hơn. Nguồn của đối tượng là một API mà tôi không có quyền kiểm soát (đã thêm mã).
Skiphoppy

2

Nếu tôi phải sử dụng API không hỗ trợ Generics .. Tôi sẽ thử và cách ly các cuộc gọi đó trong các thói quen của trình bao bọc với càng ít dòng càng tốt. Sau đó tôi sử dụng chú thích SuppressWarnings và cũng thêm các phôi an toàn loại cùng một lúc.

Đây chỉ là một sở thích cá nhân để giữ mọi thứ gọn gàng nhất có thể.


2

Lấy cái này, nó nhanh hơn nhiều so với việc tạo HashMap mới, nếu nó đã là một, nhưng vẫn an toàn, vì mỗi phần tử được kiểm tra theo kiểu ...

@SuppressWarnings("unchecked")
public static <K, V> HashMap<K, V> toHashMap(Object input, Class<K> key, Class<V> value) {
       assert input instanceof Map : input;

       for (Map.Entry<?, ?> e : ((HashMap<?, ?>) input).entrySet()) {
           assert key.isAssignableFrom(e.getKey().getClass()) : "Map contains invalid keys";
           assert value.isAssignableFrom(e.getValue().getClass()) : "Map contains invalid values";
       }

       if (input instanceof HashMap)
           return (HashMap<K, V>) input;
       return new HashMap<K, V>((Map<K, V>) input);
    }

key.isAssignableFrom(e.getKey().getClass())có thể được viết làkey.isInstance(e.getKey())
user102008

1

Chỉ cần đánh máy nó trước khi bạn đúc nó.

Object someObject = session.getAttribute("attributeKey");
if(someObject instanceof HashMap)
HashMap<String, String> theHash = (HashMap<String, String>)someObject;  

Và đối với bất cứ ai hỏi, việc nhận các đối tượng mà bạn không chắc chắn về loại này là khá phổ biến. Rất nhiều triển khai "SOA" kế thừa đi qua các đối tượng khác nhau mà bạn không nên luôn tin tưởng. (Sự kinh hoàng!)

EDIT đã thay đổi mã ví dụ một lần để phù hợp với các cập nhật của người đăng và theo dõi một số nhận xét tôi thấy cá thể đó không chơi tốt với thuốc generic. Tuy nhiên, việc thay đổi kiểm tra để xác nhận hợp lệ đối tượng bên ngoài dường như chơi tốt với trình biên dịch dòng lệnh. Sửa đổi ví dụ bây giờ được đăng.


8
Thật không may, thuốc generic khiến điều đó là không thể. Nó không chỉ là HashMap, mà còn là HashMap với thông tin loại. Và nếu tôi loại bỏ thông tin đó, tôi sẽ đẩy các cảnh báo sang nơi khác.
Skiphoppy

1

Hầu như mọi vấn đề trong Khoa học Máy tính đều có thể được giải quyết bằng cách thêm một mức độ gián tiếp *, hoặc một cái gì đó.

Vì vậy, giới thiệu một đối tượng không chung chung ở cấp độ cao hơn mà a Map. Không có bối cảnh, nó sẽ không có vẻ rất thuyết phục, nhưng dù sao:

public final class Items implements java.io.Serializable {
    private static final long serialVersionUID = 1L;
    private Map<String,String> map;
    public Items(Map<String,String> map) {
        this.map = New.immutableMap(map);
    }
    public Map<String,String> getMap() {
        return map;
    }
    @Override public String toString() {
        return map.toString();
    }
}

public final class New {
    public static <K,V> Map<K,V> immutableMap(
        Map<? extends K, ? extends V> original
    ) {
        // ... optimise as you wish...
        return Collections.unmodifiableMap(
            new HashMap<String,String>(original)
        );
    }
}

static Map<String, String> getItems(HttpSession session) {
    Items items = (Items)
        session.getAttribute("attributeKey");
    return items.getMap();
}

* Ngoại trừ quá nhiều cấp độ gián tiếp.


1
Câu nói được cho là của cố giáo sư David Wheeler. vi.wikipedia.org/wiki/ Kẻ
Stephen C

1

Đây là một cách tôi xử lý việc này khi tôi ghi đè equals()thao tác.

public abstract class Section<T extends Section> extends Element<Section<T>> {
    Object attr1;

    /**
    * Compare one section object to another.
    *
    * @param obj the object being compared with this section object
    * @return true if this section and the other section are of the same
    * sub-class of section and their component fields are the same, false
    * otherwise
    */       
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            // this exists, but obj doesn't, so they can't be equal!
            return false;
        }

        // prepare to cast...
        Section<?> other;

        if (getClass() != obj.getClass()) {
            // looks like we're comparing apples to oranges
            return false;
        } else {
            // it must be safe to make that cast!
            other = (Section<?>) obj;
        }

        // and then I compare attributes between this and other
        return this.attr1.equals(other.attr1);
    }
}

Điều này dường như hoạt động trong Java 8 (thậm chí được biên dịch với -Xlint:unchecked)


0

Nếu bạn chắc chắn rằng loại được trả về bởi session.getAttribution () là HashMap thì bạn không thể đánh máy theo loại chính xác đó, mà chỉ dựa vào việc kiểm tra HashMap chung

HashMap<?,?> getItems(javax.servlet.http.HttpSession session) {  
    HashMap<?,?> theHash = (HashMap<?,?>)session.getAttribute("attributeKey");
    return theHash;
} 

Eclipse sau đó sẽ gây bất ngờ cho các cảnh báo, nhưng tất nhiên điều này có thể dẫn đến các lỗi thời gian chạy khó có thể gỡ lỗi. Tôi sử dụng phương pháp này trong các bối cảnh không chỉ hoạt động.


0

Hai cách, một cách tránh hoàn toàn thẻ, cách còn lại sử dụng phương pháp tiện ích nhưng nghịch ngợm.
Vấn đề là Bộ sưu tập được khái quát hóa trước ...
Tôi tin rằng quy tắc của ngón tay cái là: "đúc các đối tượng một lúc" - điều này có nghĩa là gì khi cố gắng sử dụng các lớp thô trong một thế giới chung là bởi vì bạn không biết điều gì trong Bản đồ này <?,?> (và thực sự JVM thậm chí có thể thấy rằng nó thậm chí không phải là Bản đồ!), rõ ràng khi bạn nghĩ về nó mà bạn không thể sử dụng nó. Nếu bạn đã có Map <String,?> Map2 thì Hashset <String> Keys = (Hashset <String>) map2.keySet () không đưa ra cảnh báo cho bạn, mặc dù đây là "hành động đức tin" cho trình biên dịch (vì nó có thể bật ra được một TreeSet) ... nhưng nó chỉ là một đơn hành động của đức tin.

PS phản đối rằng lặp đi lặp lại như trong cách đầu tiên của tôi "là nhàm chán" và "mất thời gian", câu trả lời là "không đau không tăng": một bộ sưu tập chung được đảm bảo có chứa Map.Entry <String, String> s, và không có gì khác Bạn phải trả tiền cho sự đảm bảo này. Khi sử dụng tổng quát một cách có hệ thống khoản thanh toán này, đẹp mắt, có hình thức tuân thủ mã hóa, không phải thời gian máy!
Một trường phái suy nghĩ có thể nói rằng bạn nên đặt các cài đặt của Eclipse để tạo ra các lỗi phôi không được kiểm tra như vậy, thay vì cảnh báo. Trong trường hợp đó bạn sẽ phải sử dụng cách đầu tiên của tôi.

package scratchpad;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

public class YellowMouse {

    // First way

    Map<String, String> getHashMapStudiouslyAvoidingSuppressTag(HttpSession session) {
      Map<?, ?> theHash = (Map<?, ?>)session.getAttribute("attributeKey");

      Map<String, String> yellowMouse = new HashMap<String, String>();
      for( Map.Entry<?, ?> entry : theHash.entrySet() ){
        yellowMouse.put( (String)entry.getKey(), (String)entry.getValue() );
      }

      return yellowMouse;
    }


    // Second way

    Map<String, String> getHashMapUsingNaughtyButNiceUtilityMethod(HttpSession session) {
      return uncheckedCast( session.getAttribute("attributeKey") );
    }


    // NB this is a utility method which should be kept in your utility library. If you do that it will
    // be the *only* time in your entire life that you will have to use this particular tag!!

    @SuppressWarnings({ "unchecked" })
    public static synchronized <T> T uncheckedCast(Object obj) {
        return (T) obj;
    }


}

thực tế là bạn không có đặc quyền nhận xét không cho phép bạn chỉnh sửa câu trả lời của người khác để thêm nhận xét của bạn; bạn chỉnh sửa câu trả lời của người khác để cải thiện chúng theo định dạng, cú pháp, ..., không thêm ý kiến ​​của bạn về chúng. Khi bạn đạt tới 50 đại diện, bạn sẽ có thể nhận xét ở mọi nơi, trong khi đó tôi chắc chắn rằng bạn có thể chống lại (hoặc, nếu bạn thực sự không thể, hãy viết bình luận của bạn cho câu trả lời hiện có trong bài đăng của bạn). (lưu ý cho người khác: Tôi viết bài này vì tôi đã thấy - và bị từ chối - những bình luận được đề xuất của anh ấy - chỉnh sửa các bài đăng khác trong các công cụ kiểm duyệt)
Matteo Italia

-1

Điều này làm cho các cảnh báo biến mất ...

 static Map<String, String> getItems(HttpSession session) {
        HashMap<?, ?> theHash1 = (HashMap<String,String>)session.getAttribute("attributeKey");
        HashMap<String,String> theHash = (HashMap<String,String>)theHash1;
    return theHash;
}

1
Không, nó không có. Trên thực tế, điều này tạo ra hai cảnh báo trong đó đầu tiên có một.
Stijn de Witt

À, được rồi Không chắc tại sao tôi nghĩ vậy.
lukewm

-3

Giải pháp: Vô hiệu hóa cảnh báo này trong Eclipse. Đừng @SuppressWarnings nó, chỉ cần vô hiệu hóa nó hoàn toàn.

Một số "giải pháp" được trình bày ở trên là không phù hợp, khiến mã không thể đọc được vì mục đích ngăn chặn cảnh báo ngớ ngẩn.


9
Tôi có thể hỏi tại sao không? toàn cầu vô hiệu hóa một cảnh báo sẽ ẩn những nơi khác mà vấn đề này là có thật. thêm một @SuppressWarningskhông làm cho mã không thể đọc được.
MByD
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.