Cách truyền một kiểu làm tham số phương thức trong Java


85

Trong Java, làm cách nào bạn có thể truyền một kiểu làm tham số (hoặc khai báo dưới dạng biến)?

Tôi không muốn chuyển một thể hiện của kiểu nhưng chính kiểu đó (ví dụ: int, String, v.v.).

Trong C #, tôi có thể làm điều này:

private void foo(Type t)
{
    if (t == typeof(String)) { ... }
    else if (t == typeof(int)) { ... }
}

private void bar()
{
    foo(typeof(String));
}

Có cách nào trong Java mà không truyền một thể hiện của kiểu t không?
Hay tôi phải sử dụng hằng số int hoặc enum của riêng mình?
đây có phải là cách tốt hơn không?

Chỉnh sửa: Đây là yêu cầu cho foo:
Dựa trên kiểu t, nó tạo ra một chuỗi xml ngắn khác.
Mã trong if / else sẽ rất nhỏ (một hoặc hai dòng) và sẽ sử dụng một số biến lớp riêng.

java  types 

Bạn có thể vượt qua các loại lớp như private void foo (Class c) và sử dụng như foo (String.class)
Michael Bavin

Câu trả lời:


109

Bạn có thể vượt qua một Class<T>trong.

private void foo(Class<?> cls) {
    if (cls == String.class) { ... }
    else if (cls == int.class) { ... }
}

private void bar() {
    foo(String.class);
}

Cập nhật : cách OOP phụ thuộc vào yêu cầu chức năng. Đặt cược tốt nhất sẽ là xác định giao diện foo()và triển khai hai triển khai cụ thể foo()và sau đó chỉ cần gọi foo()triển khai bạn đang có. Một cách khác có thể là một cách Map<Class<?>, Action>mà bạn có thể gọi actions.get(cls). Đây là cách dễ dàng để được kết hợp với một giao diện và triển khai cụ thể: actions.get(cls).foo().


Tôi sẽ thêm chi tiết về yêu cầu cho câu hỏi.

Tôi đã quyết định sử dụng phiên bản đơn giản ngay bây giờ vì các loại được đề cập sẽ luôn là nguyên thủy (int, string, v.v.). Tuy nhiên, tôi chắc chắn sẽ ghi nhớ con đường oop. Cảm ơn.

Stringkhông phải là nguyên thủy trong Java;)
BalusC

@Padawan - vẫn chưa đủ để tiếp tục. Câu trả lời từ BalusC bằng cách sử dụng Bản đồ là đủ. Bạn đúng khi chấp nhận nó.
duffymo 10/02/10

Ngoài ra, bạn có thể sử dụng Class<?> clstrong một cái gì đó khác hơn là so sánh. Trong khi bạn không thể viết: cls value = (cls) aCollection.iterator().next();bạn có thể gọi cls.cast (aCollection.iterator (). Next ()); Lớp javadoc
dlamblin

17

Tôi đã có một câu hỏi tương tự, vì vậy tôi đã tạo ra một câu trả lời hoàn chỉnh có thể chạy được bên dưới. Những gì tôi cần làm là chuyển một lớp (C) cho một đối tượng (O) của một lớp không liên quan và yêu cầu đối tượng đó (O) gửi lại các đối tượng mới của lớp (C) cho tôi khi tôi yêu cầu chúng.

Ví dụ dưới đây cho thấy điều này được thực hiện như thế nào. Có một lớp MagicGun mà bạn tải với bất kỳ loại con nào của lớp Projectile (Pebble, Bullet hoặc NuclearMissle). Điều thú vị là bạn tải nó với các kiểu con của Projectile, nhưng không phải các đối tượng thực tế của kiểu đó. MagicGun tạo ra đối tượng thực tế khi đến lúc chụp.

Đầu ra

You've annoyed the target!
You've holed the target!
You've obliterated the target!
click
click

Mật mã

import java.util.ArrayList;
import java.util.List;

public class PassAClass {
    public static void main(String[] args) {
        MagicGun gun = new MagicGun();
        gun.loadWith(Pebble.class);
        gun.loadWith(Bullet.class);
        gun.loadWith(NuclearMissle.class);
        //gun.loadWith(Object.class);   // Won't compile -- Object is not a Projectile
        for(int i=0; i<5; i++){
            try {
                String effect = gun.shoot().effectOnTarget();
                System.out.printf("You've %s the target!\n", effect);
            } catch (GunIsEmptyException e) {
                System.err.printf("click\n");
            }
        }
    }
}

class MagicGun {
    /**
     * projectiles holds a list of classes that extend Projectile. Because of erasure, it
     * can't hold be a List<? extends Projectile> so we need the SuppressWarning. However
     * the only way to add to it is the "loadWith" method which makes it typesafe. 
     */
    private @SuppressWarnings("rawtypes") List<Class> projectiles = new ArrayList<Class>();
    /**
     * Load the MagicGun with a new Projectile class.
     * @param projectileClass The class of the Projectile to create when it's time to shoot.
     */
    public void loadWith(Class<? extends Projectile> projectileClass){
        projectiles.add(projectileClass);
    }
    /**
     * Shoot the MagicGun with the next Projectile. Projectiles are shot First In First Out.
     * @return A newly created Projectile object.
     * @throws GunIsEmptyException
     */
    public Projectile shoot() throws GunIsEmptyException{
        if (projectiles.isEmpty())
            throw new GunIsEmptyException();
        Projectile projectile = null;
        // We know it must be a Projectile, so the SuppressWarnings is OK
        @SuppressWarnings("unchecked") Class<? extends Projectile> projectileClass = projectiles.get(0);
        projectiles.remove(0);
        try{
            // http://www.java2s.com/Code/Java/Language-Basics/ObjectReflectioncreatenewinstance.htm
            projectile = projectileClass.newInstance();
        } catch (InstantiationException e) {
            System.err.println(e);
        } catch (IllegalAccessException e) {
            System.err.println(e);
        }
        return projectile;
    }
}

abstract class Projectile {
    public abstract String effectOnTarget();
}

class Pebble extends Projectile {
    @Override public String effectOnTarget() {
        return "annoyed";
    }
}

class Bullet extends Projectile {
    @Override public String effectOnTarget() {
        return "holed";
    }
}

class NuclearMissle extends Projectile {
    @Override public String effectOnTarget() {
        return "obliterated";
    }
}

class GunIsEmptyException extends Exception {
    private static final long serialVersionUID = 4574971294051632635L;
}

2
Tôi hiểu rằng bạn đang cố gắng trình diễn việc chuyển một loại sang một phương thức và tôi thích ví dụ này nói chung, nhưng câu hỏi đơn giản mà điều này đặt ra là tại sao bạn không nạp súng ma thuật với các phiên bản mới của đường đạn thay vì làm phức tạp các vấn đề như điều này? Cũng đừng ngăn chặn các cảnh báo, hãy sửa chúng bằng List<Class<? extends Projectile>> projectiles = new ArrayList<Class<? extends Projectile>>(). Lớp Projectile phải là một giao diện, và nối tiếp là không cần thiết ví dụ của bạn,
dlamblin

1
Lý do duy nhất mà tôi có thể nghĩ ra là việc đọc tên lớp có thể hữu ích. pastebin.com/v9UyPtWT Nhưng sau đó, bạn có thể sử dụng một enum. pastebin.com/Nw939Js1
dlamblin

10

Ồ, nhưng đó là đoạn mã không hướng đối tượng, xấu xí. Thời điểm bạn nhìn thấy "if / else" và "typeof", bạn nên nghĩ đến tính đa hình. Đây là cách đi sai lầm. Tôi nghĩ generics là bạn của bạn ở đây.

Bạn định đối phó với bao nhiêu loại?

CẬP NHẬT:

Nếu bạn chỉ nói về String và int, đây là một cách bạn có thể làm điều đó. Bắt đầu với giao diện XmlGenerator (đủ với "foo"):

package generics;

public interface XmlGenerator<T>
{
   String getXml(T value);
}

Và việc triển khai cụ thể XmlGeneratorImpl:

    package generics;

public class XmlGeneratorImpl<T> implements XmlGenerator<T>
{
    private Class<T> valueType;
    private static final int DEFAULT_CAPACITY = 1024;

    public static void main(String [] args)
    {
        Integer x = 42;
        String y = "foobar";

        XmlGenerator<Integer> intXmlGenerator = new XmlGeneratorImpl<Integer>(Integer.class);
        XmlGenerator<String> stringXmlGenerator = new XmlGeneratorImpl<String>(String.class);

        System.out.println("integer: " + intXmlGenerator.getXml(x));
        System.out.println("string : " + stringXmlGenerator.getXml(y));
    }

    public XmlGeneratorImpl(Class<T> clazz)
    {
        this.valueType = clazz;
    }

    public String getXml(T value)
    {
        StringBuilder builder = new StringBuilder(DEFAULT_CAPACITY);

        appendTag(builder);
        builder.append(value);
        appendTag(builder, false);

        return builder.toString();
    }

    private void appendTag(StringBuilder builder) { this.appendTag(builder, false); }

    private void appendTag(StringBuilder builder, boolean isClosing)
    {
        String valueTypeName = valueType.getName();
        builder.append("<").append(valueTypeName);
        if (isClosing)
        {
            builder.append("/");
        }
        builder.append(">");
    }
}

Nếu tôi chạy điều này, tôi nhận được kết quả sau:

integer: <java.lang.Integer>42<java.lang.Integer>
string : <java.lang.String>foobar<java.lang.String>

Tôi không biết đây có phải là điều bạn đã nghĩ hay không.


Hiện tại chỉ có int và string. Cách tốt nhất để làm điều này là gì?

4
"Bất cứ lúc nào bạn thấy mình đang viết mã của biểu mẫu" nếu đối tượng thuộc loại T1, thì hãy làm điều gì đó, nhưng nếu nó thuộc loại T2, thì hãy làm điều gì đó khác, "tự tát mình." javapractices.com/topic/TopicAction.do?Id=31
Pool

@ The Feast: cảm ơn vì liên kết. Tôi nghĩ tôi hiểu những gì đang được nói nhưng ở đây tôi không có ví dụ về bất kỳ đối tượng nào. Tôi muốn foo tạo một chuỗi khác cho một chuỗi int so với một chuỗi. Tôi có phải tạo hai phiên bản của các đối tượng bắt nguồn từ một số đối tượng khác để thực hiện theo cách tốt nhất không? Nó có thể là quá mức cần thiết cho trường hợp này?

Tôi đã quyết định sử dụng phiên bản đơn giản ngay bây giờ vì các loại được đề cập sẽ luôn là nguyên thủy (int, string, v.v.). Tuy nhiên, tôi chắc chắn sẽ ghi nhớ con đường oop. Cảm ơn.

1
@Padawan, xin lỗi, tôi không có ý hạ mình - nhận xét của duffymo nhắc nhở tôi về câu trích dẫn đó là tất cả!
Hồ bơi

9

Bạn nên vượt qua một Class...

private void foo(Class<?> t){
    if(t == String.class){ ... }
    else if(t == int.class){ ... }
}

private void bar()
{
   foo(String.class);
}

Tại sao bạn nên vượt qua một lớp chứ không phải một loại? Có một số lý do mà chữ ký như method_foo(Type fooableType)ít hữu ích hơn method_foo(Class<?> fooableClass)?
roberto tomás

5

Nếu bạn muốn chuyển kiểu, thì kiểu tương đương trong Java sẽ là

java.lang.Class

Nếu bạn muốn sử dụng một phương pháp nhập yếu, thì bạn chỉ cần sử dụng

java.lang.Object

và toán tử tương ứng

instanceof

ví dụ

private void foo(Object o) {

  if(o instanceof String) {

  }

}//foo

Tuy nhiên, trong Java có các kiểu nguyên thủy, không phải là lớp (tức là int từ ví dụ của bạn), vì vậy bạn cần phải cẩn thận.

Câu hỏi thực sự là bạn thực sự muốn đạt được điều gì ở đây, nếu không thì rất khó trả lời:

đây có phải là cách tốt hơn không?


0

Bạn có thể chuyển một thể hiện của java.lang.Class đại diện cho kiểu, tức là

private void foo(Class cls)
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.