Это то, что я делаю, чтобы печатать различные версии в файле журнала. Я жестко закодировал расширенный путь, но приложения могут использовать servletContext.getRealPath("/")
для чтения полного пути к папке webapp. Может печатать только предоставленные библиотеки или все, что угодно из папки lib.
// print library versions (jersey-common.jar, jackson-core-2.6.1.jar)
try {
List<String> jars = Arrays.asList( "jersey-common", "jackson-core", "openjpa", "mylib" );
StringBuilder verbuf = new StringBuilder();
for(File file : new File("/opt/tomcat/webapps/myapp/WEB-INF/lib/").listFiles() ) {
String name = file.getName();
if (file.isDirectory() || !file.isFile() || !name.endsWith(".jar") ) continue;
name = name.substring(0, name.length()-4);
boolean found = jars.contains(name);
if (!found) {
int idx = name.lastIndexOf('-');
if (idx>0)
found = jars.contains( name.substring(0, idx) );
}
if (!found) continue;
JarFile jarFile = new JarFile(file, false);
try {
String ver;
Manifest mf = jarFile.getManifest();
if (mf!=null) {
ver = mf.getMainAttributes().getValue("Bundle-Version");
if (ver==null || ver.isEmpty())
ver = mf.getMainAttributes().getValue("Implementation-Version");
} else ver=null;
if (verbuf.length()>0) verbuf.append(", ");
verbuf.append(name + "=" + (ver!=null?ver:"") );
} finally {
jarFile.close();
}
}
System.out.println( verbuf.toString() );
} catch(Exception ex) {
ex.printStackTrace();
}
Всякий раз, когда метод хочет изменить получателя, он должен быть указателем на значение; метод должен иметь приемник указателя.
Если у метода есть приемник без указателя, копия будет выполнена и передана при вызове этого метода. С этого момента, независимо от того, что вы делаете с получателем, вы можете изменить только копию, а не оригинал.
Итак, в вашем примере:
func (p Person) SetName(newName string) {
name := p.GetName();
*name = newName
}
Когда SetName()
, копия сделана из Person
. Внутри SetName()
вы получите адрес поля name
копии, которую вы изменяете. (На самом деле, копия копии, подробнее об этом позже ...)
Решение: используйте приемник-указатель:
func (p *Person) SetName(newName string) {
name := p.GetName();
*name = newName
}
Из этого point, только *Person
реализует Individual
, поэтому используйте указатель при добавлении:
individuals = append(individuals, &person)
Это сложно, потому что после этого он не будет работать. Почему это?
Это потому, что у Person.GetName()
все еще есть приемник без указателя:
func (p Person) GetName() *string {
return &p.name
}
Поэтому, когда GetName()
вызывается из SetName()
, копия , и GetName()
вернет адрес поля name
копии, а SetName()
изменит только копию, созданную для вызова GetName()
.
Итак, чтобы сделать все вы также должны использовать приемник указателей для GetName()
:
func (p *Person) GetName() *string {
return &p.name
}
И теперь он работает, выводит (попробуйте на Go Playground ):
{Steve}
&{Peter}
{Peter}
Но знайте, что самый простой и рекомендуемый способ - просто:
func (p *Person) SetName(newName string) {
p.name = newName
}
Это все, что нужно.
См. https://play.golang.org/p/eg8oYDV6Xx p1 и p2 уже являются адресами, вам не нужно их адрес (адрес адреса), просто распечатайте p1 и p2 - они будут такими же, как вы можете видеть.
Две вещи, чтобы исправить это:
Person
, так как им необходимо реализовать интерфейс type Individual interface {
GetName() *string
SetName(name string)
}
type Person struct {
name string
}
// Implement functions of the Individual interface
func (p Person) GetName() *string {
return &p.name
}
func (p *Person) SetName(newName string) {
p.name = newName
}
var individuals []Individual
func main() {
person := &Person{name: "Steve"}
fmt.Println(person)
individuals = append(individuals, person) // append Person to slice
p1 := individuals[0] // Retrieve the only Person from slice
p1.SetName("Peter") // Change name
fmt.Println(p1) // Prints "Steve"
fmt.Println(person) // Prints "Steve"
}
Пример на Go Playground .
GetName
немного вводит в заблуждение. Он возвращает указатель на string
в копии Person
. Я предполагаю, что метод должен либо иметь приемник указателя, либо возвращать значение, нет?
– Chris Drew
10 May 2017 в 11:28
GetName
было бы более идиоматично возвращать значениеstring
? – Chris Drew 10 May 2017 в 11:26p1 := individuals[0]
, а затем напечатаю адрес сfmt.Println(&p1)
после каждого вызова, я получаю разные адреса каждый раз.p2 := individuals[0]
, а затемfmt.Println(&p2)
напечатает другой адрес. Зачем? – Rox 10 May 2017 в 11:36p1 := individuals[0]
является short variable declaration , который создает локальную переменную, а&p1
является адресом локальной переменной i> (а не значением указателя, если она содержат указатель), который будет (может) отличаться каждый раз. – icza 10 May 2017 в 11:38